# Deploy a Docker image
URL: https://synquic.tech/docs/guides/deploy-docker-image.md

Run a prebuilt container image from Docker Hub, GitHub Container Registry or any private registry. Nothing is cloned or built. Covers registry credentials, ports, environment variables and tracking new versions.

import { Step, Steps } from 'fumadocs-ui/components/steps';

When your app is already published as a container image (by your CI, or by the project that makes it),
Synq can run that image directly. There is no repository to clone and no build step: Synq pulls the image
on your server, starts it, and routes traffic to it like any other project.

Use this for:

- an image your CI pushes to a registry, such as `ghcr.io/acme/api:1.4.2`;
- an off-the-shelf image, such as `nginx:1.27` or `louislam/uptime-kuma:1`;
- anything in a **private** registry, with a registry login Synq stores for you.

<Callout title="A whole stack of images?">
For several containers that work together (an app plus its database), paste a Compose file instead:
see [Paste a Dockerfile or Compose file](/docs/guides/deploy-dockerfile-or-compose#deploy-a-pasted-compose-file).
For a ready-made stack, look at the [one-click apps](/docs/guides/one-click-apps) first.
</Callout>

## Deploy an image

<Steps>

<Step>

### Open the Docker image tab

Click **New Project** in the sidebar, then the **Docker image** tab. The card is titled
**Deploy a Docker image** ("Run a prebuilt image from Docker Hub or any registry. Nothing is cloned or
built."). You can also open `/library#image` directly.

</Step>

<Step>

### Enter the image

Type the image reference into **Image**:

| You type | Pulled from |
|---|---|
| `nginx:1.27` | Docker Hub (`docker.io/library/nginx`) |
| `myorg/web:2.0` | Docker Hub |
| `ghcr.io/acme/api:1.4.2` | GitHub Container Registry |
| `registry.example.com:5000/team/app:3.1` | Your own registry on port 5000 |

The tag is optional and defaults to `latest`. A digest (`image@sha256:...`) is not accepted here: use a
tag. **Project name** fills in from the image name; change it if you like. Project names must be unique:
an existing project with the same name is not overwritten.

</Step>

<Step>

### Set the container port

**Container port** is the port the app listens on **inside** the container (default `80`). Check the
image's documentation: `nginx` listens on 80, many Node images on 3000, many Go and Java images on 8080.
Synq routes your domain to this port.

</Step>

<Step>

### Pick the server

**Server** chooses which of your servers runs the image. It starts on the first server in the list.

</Step>

<Step>

### Add a registry login (private images only)

Turn on **Private registry** and fill in:

- **Username**: your registry user (for GitHub Container Registry, your GitHub username).
- **Password or access token**: prefer a read-only token over an account password. For GitHub Container
  Registry, a token with the `read:packages` scope; for Docker Hub, a personal access token with read
  access.

Synq saves this as an organization **registry credential** for that registry host ("Saved as a registry
credential for ghcr.io and used for every pull from it"). If a credential for that host already exists,
the form says "Using the saved credential for ghcr.io" and asks for nothing.

Saving a new registry login needs organization **admin** rights; for other members the whole form fails
with "Could not create the project". If you are not an admin, ask one to add the login under
**Settings → Credentials** first; the form then uses it.

</Step>

<Step>

### Add environment variables (optional)

**Environment variables** takes one `KEY=value` per line, as in a `.env` file:

```bash
DATABASE_URL=postgres://app:secret@db.internal:5432/app
LOG_LEVEL=info
```

You can change them later on the project's **Environment** tab.

</Step>

<Step>

### Press Create and deploy

Synq creates the project, saves the variables and starts the first deploy. You land on the build screen,
which shows the pull and start. When it is live, add a domain on the project's **Domains & Routes** tab
(see [Custom domains](/docs/guides/custom-domains)).

</Step>

</Steps>

If the project was created but the first deploy could not start, you see "The project was created, but
the first deploy could not start" and land on the project, where you can fix the problem and press
**Deploy**.

## Registry credentials

Registry logins are shared by the whole organization and used for **every** pull from that registry host:
image projects, Compose services and one-click apps alike. Manage them in **Settings → Credentials**
(organization admins only):

1. Under **Container registry** ("Pull private images from a registry during deploys"), press **Add**.
2. **Registry host**: the host as it appears in your image reference, such as `ghcr.io`,
   `registry.example.com:5000`, or `docker.io` for Docker Hub. `https://` and trailing slashes are
   removed, and every Docker Hub spelling is stored as `docker.io`.
3. **Username** and **Password or access token**, then save. Synq checks the credential with the registry
   before saving it. A saved login that later stops working is marked **Needs attention**; after fixing
   it, use **Re-check with the provider**.

Credentials are stored encrypted and never shown again. To rotate one, edit it and type the new secret; to
stop using it, delete it (anything pulling from that registry privately stops working).

## Track new versions

An image project remembers **which** version it runs. Open the project, then
**Source & Triggers → Source**. The **Tracked container image** card shows the **Image** template, the
**Version source**, the **Current version**, and the **Latest version** (or **Target version** when a
version is pinned), with **Update available** when you are behind.

The **Release tracking** form below it decides where new versions come from:

- **Image template**: the image with a placeholder for the version, such as `ghcr.io/acme/api:{tag}`.
  `{tag}` keeps the publisher's exact tag (`v2.4.1`); `{version}` uses the normalized version (`2.4.1`).
- **Version provider**:
  - **GitHub Releases**: follow the latest published release of a **GitHub repository** (`owner/repository`).
  - **Version URL or pin**: read an HTTPS **Version URL** that returns the version as text, or JSON with
    `version` or `tag_name`, or deploy a fixed version only.
- **Pinned version**: a fixed version to deploy, such as `v2.4.1`. It overrides the discovered latest
  version; leave it blank to track updates.

Press **Save source**. Changes apply to every environment of the project. A project created from the
**Docker image** tab starts as a pinned version (the tag you typed).

When a newer version is found, the project's **Deployments** tab shows **New version available** with a
**Deploy new version** button.

The same settings from the terminal:

```bash
# Follow GitHub Releases of acme/api, deploying ghcr.io/acme/api:<release tag>
synq project release-image <project-id> --image-template "ghcr.io/acme/api:{tag}" --github-repo acme/api

# Or deploy one fixed version
synq project release-image <project-id> --image-template "ghcr.io/acme/api:{tag}" --pin v1.4.2
```

## Redeploy when CI pushes a new image

If your CI pushes a new image to the same tag (for example `:main` or `:latest`), create a
[deploy hook](/docs/guides/deploy-hooks) and call it at the end of the pipeline. A deploy started by a
deploy hook pulls registry images again instead of reusing the copy already on the server, so it picks up
the new push. For anything you may need to roll
back, prefer versioned tags: [rollback](/docs/guides/rollback-redeploy) can then restore the exact image a
release ran.

## Switch between an image and a repository

- An image project's **Source** card has **Link Git repository** to switch it to a Git source. Nothing
  changes until you actually link a repository.
- A Git project that could run a published image instead offers **Use prebuilt image** on its **Source**
  card. Saving replaces the Git source for every environment; existing deployments and rollback history
  stay available.

## Limits

- One image per project. For several containers, use a [Compose file](/docs/guides/deploy-dockerfile-or-compose).
- The image must run on your server's CPU architecture (for example `linux/amd64` or `linux/arm64`).
- Image projects have no build, so the **Builder** setting does not apply to them.
- The container's own start command (`CMD`/`ENTRYPOINT`) starts the app.

## Related

<Cards>
  <Card title="Paste a Dockerfile or Compose file" href="/docs/guides/deploy-dockerfile-or-compose" description="Build from a pasted Dockerfile, or run a pasted docker-compose.yml." />
  <Card title="Deploy hooks" href="/docs/guides/deploy-hooks" description="Redeploy from CI with a URL call." />
  <Card title="Rollback & redeploy" href="/docs/guides/rollback-redeploy" description="Return to an earlier image in one click." />
</Cards>
