Deploy Basics
The simple deployment pattern
Section titled “The simple deployment pattern”The most straightforward deployment: SSH to your server, pull the new image, and start a container.
# On the deployment serverdocker pull acme/api-server:2.1.0docker stop api-server || truedocker rm api-server || truedocker run -d \ --name api-server \ --restart unless-stopped \ -p 3000:3000 \ -e DATABASE_URL="$DATABASE_URL" \ acme/api-server:2.1.0The || true prevents the pipeline from failing if the container does not yet exist on a first deploy.
Restart policies
Section titled “Restart policies”The --restart flag tells Docker what to do if the container exits:
| Policy | Behaviour |
|---|---|
no (default) | Never restart |
on-failure | Restart only on non-zero exit |
always | Always restart, including after docker restart daemon |
unless-stopped | Like always but does not restart if you explicitly docker stop it |
For production services, use unless-stopped. It survives host reboots and daemon restarts but respects intentional stops.
Rolling updates with Docker Compose
Section titled “Rolling updates with Docker Compose”If your server already runs services with Compose v2, updates are a two-command operation:
# Pull all updated images defined in compose.ymldocker compose pull
# Recreate containers that have a newer imagedocker compose up -dCompose recreates only the services whose image digest has changed, leaving unchanged services running. If you use a compose.yml with pinned image tags:
services: api: image: acme/api-server:2.1.0 restart: unless-stopped ports: - "3000:3000" env_file: - .envChange the tag in compose.yml and run docker compose up -d — Compose detects the tag change and recreates the service.
Environment variables and secrets on the host
Section titled “Environment variables and secrets on the host”Never bake secrets into your image. Instead, pass them at runtime.
Three patterns, from simplest to most robust:
1. Environment variables via -e
Section titled “1. Environment variables via -e”docker run -d \ -e DATABASE_URL="postgres://user:pass@db:5432/app" \ -e SECRET_KEY="abc123" \ acme/api-server:2.1.0Simple, but the values appear in docker inspect output and shell history.
2. Environment file via --env-file
Section titled “2. Environment file via --env-file”# .env (not committed to git)DATABASE_URL=postgres://user:pass@db:5432/appSECRET_KEY=abc123docker run -d --env-file .env acme/api-server:2.1.0Cleaner than -e flags. Keep the .env file in a secrets manager or vault on the server.
3. Docker secrets (Swarm / Compose)
Section titled “3. Docker secrets (Swarm / Compose)”For teams running Docker Swarm or Compose with the secrets: block, secrets are mounted as files at /run/secrets/<name> inside the container. This is the most secure option — secrets are never exposed as environment variables.
services: api: image: acme/api-server:2.1.0 secrets: - db_password
secrets: db_password: external: trueVerifying the deployment
Section titled “Verifying the deployment”After pulling and restarting, confirm the new version is running:
docker psdocker logs api-server --tail 20docker inspect api-server | grep ImageHands-on: pull and run a public image with a restart policy
Section titled “Hands-on: pull and run a public image with a restart policy”The snippet below demonstrates docker pull, docker run -d --restart unless-stopped, and log inspection — using a public image so no credentials are needed.
# Pull a versioned nginx image
docker pull nginx:1.27-alpine
# Run it detached with a restart policy
docker run -d \
--name web \
--restart unless-stopped \
-p 8080:80 \
nginx:1.27-alpine
# Verify it is running
docker ps
# Check logs
docker logs web --tail 5
# Simulate an update: pull a different tag and recreate
docker pull nginx:1.26-alpine
docker stop web && docker rm web
docker run -d \
--name web \
--restart unless-stopped \
-p 8080:80 \
nginx:1.26-alpine
docker ps