19 October 2026

Deploy Docker Compose with Coolify, update it, and roll back a commit

Deploy the exact revision you intended, verify the visible version, and practise returning to the original commit before storing real data.

Deploy Docker Compose with Coolify, update it, and roll back a commit

Deploy a small static app from a known Git commit, check the running page, then practise a pinned update and rollback. Pinning makes the intended source explicit, but you still need health and page checks after each deployment.

Watch the original video

Part 6: Deploy Docker Compose with Coolify, update it, and roll back a commit

Video draft: Recorded Coolify deployment, update and pinned rollback. The GitHub commit was prepared before recording; build waits are shortened. Access uses a private SSH tunnel. Audio, captions and editing should receive a full review before publication.

Create these four files

Use a new repository-root directory with compose.yaml, Dockerfile, nginx.conf, and site/index.html. The tutorial image is pinned to the reviewed nginx manifest. Check current upstream security updates before using it for a long-lived service. The package uses container port 8080 and no published host port.

compose.yaml

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    expose:
      - "8080"
    read_only: true
    tmpfs:
      - /tmp:size=16m,mode=1777
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"

Dockerfile

# Official nginx stable-alpine manifest verified on Docker Hub, 2026-10-18.
FROM nginx:stable-alpine@sha256:970364228b1e4f932abc22d321932816782cca8a20b85a77552a7cfe9acfb3b5
COPY nginx.conf /etc/nginx/nginx.conf
COPY site/ /usr/share/nginx/html/
USER nginx
EXPOSE 8080
# Bypass root-oriented entrypoint scripts; our config is ready to run.
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

nginx.conf

worker_processes auto;
pid /tmp/nginx.pid;
error_log /dev/stderr warn;
events { worker_connections 256; }
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log /dev/stdout;
    server_tokens off;
    client_body_temp_path /tmp/client_temp;
    proxy_temp_path /tmp/proxy_temp;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;
    server {
        listen 8080;
        root /usr/share/nginx/html;
        index index.html;
        add_header X-Content-Type-Options nosniff always;
        add_header Referrer-Policy no-referrer always;
        location / { try_files $uri $uri/ =404; }
        location = /healthz { access_log off; default_type text/plain; return 200 "ok\n"; }
    }
}

site/index.html

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>My first self-hosted app</title></head>
<body><main><span class="version">Version 1.0</span><h1>Hello from my VPS.</h1><p>Deployment checklist: confirm the source revision, container health, and actual page.</p></main></body></html>

Add .gitignore and .dockerignore entries for .git, .env, and .env.*. If supplying a safe example, allow !.env.example. This app requires no secrets. Do not add a real environment file to Git or the image.

Deploy the known revision

  1. Commit the files and record the full SHA. Check the read-only private repository connection from part 5.
  2. Create a Coolify project and environment, then an Application from the private Git repository. Choose the Docker Compose build pack.
  3. Set the base directory to / and the Compose file to /compose.yaml. Parse or inspect the service configuration.
  4. Select the intended branch and full commit SHA where the installed version exposes revision pinning. Save the source settings.
  5. Deploy once. Watch the build/deployment log, then wait for the service health check.
  6. Check /healthz returns ok and the page displays Version 1.0. Verify the deployed commit matches your intended SHA.

Coolify: Docker Compose describes the Git-based Compose build path. A Docker Compose Application and an empty Compose Service are different routes. This lesson uses the Git-based Application so the revision is visible.

Reach the app

For a public app, configure the web service’s domain in Coolify to target internal port 8080, point DNS at the server, and verify HTTPS. Do not add ports: merely to bypass the proxy. The recorded tutorial kept the app private: it read the running container’s private IP and forwarded a laptop loopback port to it:

On the VPS, first list running containers and identify the one belonging to the intended Coolify application. Match the name to that app’s deployment log or app identifier, because a server can have more than one service named web. Inspect only the selected runtime fields:

docker ps --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}'
APP_CONTAINER='paste-the-actual-tutorial-app-container-name'
docker inspect --format '{{.State.Health.Status}}' "$APP_CONTAINER"
docker inspect --format '{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' "$APP_CONTAINER"

Choose the private address for the app’s actual Docker network. Do not paste a full docker inspect dump into shared notes because configuration can contain secrets. From the laptop, replace CONTAINER_IP and SERVER_IP in this tunnel command with the observed values:

ssh -N -L 127.0.0.1:18089:CONTAINER_IP:8080 -i ~/.ssh/hetzner_tutorial root@SERVER_IP

Read CONTAINER_IP from the actual running app container with docker inspect, and recheck it after container replacement. Open http://127.0.0.1:18089. This verifies private HTTP through SSH, not public TLS.

Update, then return to the original commit

  1. Change only the badge from Version 1.0 to Version 1.1, commit, and push. Record the new full SHA.
  2. Set Coolify’s selected revision to that SHA, save, and deploy. Check the deployment result, running revision, /healthz, and visible Version 1.1.
  3. For the pinned rollback exercise, set the selected revision back to the recorded original SHA, save, and deploy again.
  4. Confirm Version 1.0, health, and the original running SHA. Keep the update commit in Git history for inspection.

The tutorial verified this exact manual update-and-pinned-rollback pattern. Automatic webhook deployment was not enabled. Another source-level option is git revert UPDATE_COMMIT, pushing and deploying the new revert commit. Neither option reverses a database migration or recovers deleted user data.

If deployment succeeds but the page fails

Check the container’s health log, nginx listen port, selected Compose path, proxy target, and actual route. A green build means the image built; it does not prove that DNS, TLS, or the browser route works. Keep the last known-good SHA until the update is checked.

Return to the Hetzner and Coolify series

When the checks in this lesson pass, continue with part 7.