Image deploys

Image deploys hand the stack a pre-built Docker image instead of source code. The stack pulls the image and runs it. Faster deploys, build-once-deploy-many semantics, and your CI gets to build images on bigger machines than your stack.

When to pick image deploys over git deploys

Pick image deploys whenPick git deploys when
Builds are slow and you want them off the stackYou want one obvious source of truth: your branch tip
You're already building images in CIYou don't have a registry yet
You want to deploy the same image to multiple stacks atomicallyYou're using buildpacks and don't want a Dockerfile
You need build secrets (npm tokens, private package registries) you don't want on the stackYou're prototyping and the build is fast enough on the stack anyway

Trigger

$ ownstack deploy --image                              # latest tag from registry default
$ ownstack deploy --image=ghcr.io/me/my-app:v1.2.3

The image must be pullable from the stack. For private registries, configure the credential in the dashboard (Stack → Registry credentials) once; the stack's docker daemon uses it.

Image requirements

  • Web process binds to $PORT — the stack tells the container which port to listen on at runtime, just like buildpack apps.
  • One CMD / one ENTRYPOINT per image — corresponds to the web process. For multi-process apps, declare process types via app.json's scripts or via Procfile-in-image.
  • No HEALTHCHECK required — dokku adds its own probe.
  • Linux/amd64 by default. ARM64 support depends on the stack's instance architecture.

Multi-arch images

If you build with docker buildx for both amd64 and arm64, the registry stores a manifest list and the stack pulls the matching arch automatically. Your CI looks like:

docker buildx build --platform linux/amd64,linux/arm64 \
  -t ghcr.io/me/my-app:$SHA \
  -t ghcr.io/me/my-app:latest \
  --push .

Tagging strategy

Two patterns work well:

  • Immutable SHA tags: tag with $GITHUB_SHA and deploy that tag. Easy rollback — old SHAs are still in the registry.
  • Latest + version: tag both latest and v1.2.3; deploy v1.2.3 for prod, latest for staging.

Don't deploy latest to prod without a version pin. latest moves; reproducible deploys care which SHA was running.

CI hookup (GitHub Actions example)

name: Deploy
on:
  push: { branches: [main] }
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - run: |
          docker buildx build --push \
            --platform linux/amd64 \
            -t ghcr.io/${{ github.repository }}:${{ github.sha }} \
            -t ghcr.io/${{ github.repository }}:latest .
      - run: |
          curl -sSL https://ownstack.org/cli/install.sh | bash
          export PATH="$HOME/bin:$PATH"
          ownstack login --token ${{ secrets.OWNSTACK_TOKEN }}
          ownstack deploy --app=my-app --image=ghcr.io/${{ github.repository }}:${{ github.sha }}