Skip to content

Deploying OCI images

Use app.type: dockerimage when you already have a runnable Linux OCI image and do not want APCO to receive source or perform a build. APCO validates the registry, pulls the image, resolves a tag to an immutable repository digest, and starts it with the same gVisor, network, resource, health, and Caddy boundaries as a source-built SSR app.

Create the manifest

The starter uses a harmless public nginx image:

bash
apco init --template dockerimage
apco manifest validate --json
apco deploy --json --yes

Its manifest is deliberately separate from a source manifest:

yaml
version: 2
app:
  name: image-app
  type: dockerimage
  port: 8080
  health:
    path: /
    timeoutSeconds: 60
image:
  reference: nginxinc/nginx-unprivileged:1.29-alpine

app.type must be explicitly dockerimage; auto never infers it. app.port and image.reference are required. Source/build fields (source, buildCommand, startCommand, publishDirectory, packageManager, and dev) are rejected, and the CLI sends JSON rather than a source archive. GitHub App source bindings do not create OCI deployments; GitHub Actions may run the ordinary CLI with a one-project deploy credential.

References and digest pinning

A reference must name a publicly routed registry/repository and include either an explicit tag or a lowercase sha256 digest. APCO rejects implicit latest, URL syntax, IP literals, localhost/private/link-local/metadata addresses, APCO platform domains, and DNS answers containing a blocked address. Docker Hub shorthand is canonicalized—for example, nginxinc/nginx-unprivileged:1.29-alpine becomes registry-1.docker.io/nginxinc/nginx-unprivileged:1.29-alpine.

A tag is used only for admission. After the pull, APCO verifies and stores the registry's immutable repository digest, Linux OS, worker architecture, entrypoint/command, and image size. Promotion, Production rollback, start, scheduled jobs, and local-cache recovery use that digest, never the mutable tag. Redeploy the manifest to deliberately resolve a tag again.

The plan's maxImageMb is enforced during transfer and again against the inspected image size; 0 disables OCI project creation/deployment. Pulls time out after 15 minutes. The image must target Linux and the worker's current architecture and must provide a usable entrypoint or command.

Private registries

Credentials are project-scoped and selected by the reference's exact normalized registry host (including a non-default port). Configure at most five hosts in Project settings or with the CLI:

bash
apco registry set ghcr.io --username my-user --project my-image-project
apco registry list --project my-image-project --json
apco registry remove ghcr.io --project my-image-project --json --yes

registry set reads the password/token from a hidden TTY prompt or stdin; it has no secret flag. Use a read-only pull credential from a secret manager for automation. Secrets and usernames are encrypted at rest, decrypted only by the worker for the exact pull, and never returned by API, CLI, MCP, dashboard, or admin responses. List results contain only host/configured/timestamps. Setting the same host rotates it atomically; removing it requires explicit confirmation.

Registry authentication does not enter the app container, release/job containers, deployment manifest, environment variables, build/runtime logs, or Docker config on disk. A registry error is reduced to a safe machine code and generic message rather than returning the registry's raw response.

Channels and lifecycle

OCI images support Production and Preview. Preview has its normal isolated URL and can be promoted without pulling a tag or rebuilding. A superseded Production deployment can be rolled back by exact digest. Stop/start also recovers the stored digest if the worker's local image cache was pruned. OCI projects do not support Dev or GitHub App PR source deployments.

Managed databases, release commands, and scheduled jobs remain available because they run from the pinned image. Database and release behavior is channel-exact; Preview data is never copied to Production. OCI job definitions may target Production or Preview, not Dev. The image must contain /bin/sh for release/job commands.

Runtime trust boundary

Treat a third-party image as executable code. Prefer a narrow image you build, scan, and pin; restrict registry credentials to pull-only access; and review a tag change before redeploying. APCO does not yet enforce signature verification or vulnerability policy.

An OCI app receives no Docker socket, host network, host registry credential file, control-plane credential, or cross-project access. It runs with gVisor, the plan's CPU/memory/process limits, APCO's tenant networks and egress policy, the same health-gated candidate activation as SSR, and only its channel-scoped environment/database. Supporting existing images does not weaken those boundaries.

Safe failure codes stored on deployment state include OCI_PLAN_UNAVAILABLE, OCI_REGISTRY_BLOCKED, OCI_CREDENTIAL_INVALID, OCI_AUTH_FAILED, OCI_PULL_FAILED, OCI_PULL_TIMEOUT, OCI_IMAGE_TOO_LARGE, OCI_DIGEST_MISMATCH, OCI_PLATFORM_UNSUPPORTED, OCI_ENTRYPOINT_MISSING, OCI_INSPECT_FAILED, OCI_DEPLOY_FAILED, and OCI_HEALTH_CHECK_FAILED. Inspect apco status --json and apco logs --json; never retry authentication or policy failures unchanged.

APCO Cloud — ship apps with one command.