> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instapods.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy a Docker image

> Run an image you already publish (GitHub Container Registry, Docker Hub, Quay, ECR Public) on a pod, with its own command, port, volumes and environment.

Already push a Docker image for your app? Run it on a pod as it is. InstaPods pulls the image,
runs its own `ENTRYPOINT` and `CMD`, routes your pod's HTTPS URL to the port it exposes, and keeps
the previous version on hand for an instant rollback. No Dockerfile changes and no build step.

```bash theme={null}
instapods deploy --image ghcr.io/you/my-api:1.4
```

Or open **Import → Docker image** in the dashboard (`/dashboard/import?tab=image`).

> [!NOTE]
>
> This runs images you have **already built**. Building a Dockerfile from your repository is not
> supported yet.

## What works

| | |
| - | - |
| **Registries** | Any public OCI registry: `ghcr.io`, Docker Hub (`docker.io`, or just `nginx:alpine`), `quay.io`, `public.ecr.aws`, and others. |
| **Images** | Public images with a `linux/amd64` build. Multi-arch images work; an arm64-only image is refused with how to fix it. |
| **Plans** | **Build** or higher. Most images need more than Launch's 512 MB. |
| **Size** | Up to 3 GB compressed. |
| **Services** | One image per pod, one routed port. |

## How your image runs

* **Command.** The image's own `ENTRYPOINT` + `CMD`, exactly as Docker would run them, in the
  image's `WORKDIR`, as the image's `USER` (root when it sets none).
* **Port.** We route your URL to the port the image `EXPOSE`s. When it exposes several, an HTTP-ish
  one wins over SSH or database ports. With no `EXPOSE`, the app gets `PORT=8080`. If the app
  ignores `PORT` and listens on a port of its own, the deploy finds that port at the end of its
  2-minute wait and routes it. To pick
  the port yourself: `--port 3000` (CLI) or the Port field in the dashboard.
* **Environment.** The image's own `ENV` values, then `PORT` and `HOST=0.0.0.0`, then the variables
  you set (`instapods env set`, or the Env tab), which win over the image's.
* **Data.** Paths the image declares as `VOLUME` are kept in `/home/instapod/data` and survive
  every redeploy and rollback, and they are backed up. Anything the app writes **outside** its
  volumes is replaced on the next deploy, as it would be with Docker.
* **Databases.** A managed PostgreSQL, MySQL or Redis runs in the same pod, and the app reaches it at
  `127.0.0.1`. Pick one when you create the pod (the **Add a managed PostgreSQL database** box in the
  dashboard) and `DATABASE_URL` / `REDIS_URL` are set for you. Added later with
  `instapods services add`, it is not: set the URL yourself with `instapods env set`. Connect over
  TCP; Unix sockets aren't shared with the app.
* **Logs.** Everything the app prints is in the Logs tab and `instapods logs <pod>`.
* **Health.** A deploy only switches over once the new version answers HTTP (it waits up to 2
  minutes, for slow starters like the JVM). If it never does, the previous version keeps serving and
  the deploy log says why.

## Redeploy, change the tag, roll back

```bash theme={null}
# Pull the same tag again (picks up a new push to :latest)
instapods deploy my-api --image ghcr.io/you/my-api:latest

# Move to a new version
instapods deploy my-api --image ghcr.io/you/my-api:1.5
```

The pod's **Image** tab does the same, and lists every deploy. **Roll back** on the previous
version is instant: it is still on the pod, so nothing is downloaded.

## Inside the pod

SSH and the web terminal land in the pod as usual. Your image's files are at
`/opt/instapods/rootfs/current`; to get a shell inside them, run
`sudo chroot /opt/instapods/rootfs/current sh` (if the image has a shell).

## Not supported yet

* Private images that need a registry login.
* Building from a `Dockerfile` or `docker-compose.yml`, and running several services from one.
* `HEALTHCHECK`, more than one routed port, UDP.
* Anything that needs extra privileges at runtime (`--privileged`, `--cap-add`, mounting
  filesystems, the Docker socket).
* `linux/arm64`-only images.

## When a deploy fails

The deploy log ends with what went wrong and what to do. The common ones:

| You see | What to do |
| - | - |
| That image or tag doesn't exist | Check the name and tag; `docker pull <image>` should work without logging in. |
| That image is private or doesn't exist | Make the package public (GitHub: package settings → Change visibility). |
| This image has no build for our servers | Build for amd64: `docker buildx build --platform linux/amd64 -t <image> --push .` |
| Your app is listening on a different port | Deploy again with `--port <port>`. |
| Your app only listens on 127.0.0.1 | Make it listen on `0.0.0.0` (most apps read `HOST`). |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.