RollHook

Zero-downtime rolling Docker Compose deployments via webhooks.

Two things. That's the integration.

Your CI workflow and a compose file — no server config, no registration, no YAML on the VPS.

1. Trigger from CI

Build your image and call RollHook in the same workflow step.

.github/workflows/deploy.yml
- name: Build & push
  uses: docker/build-push-action@v6
  with:
    push: true
    tags: ghcr.io/you/my-api:${{ github.sha }}

- name: Deploy via RollHook
  uses: jkrumm/rollhook-action@v1
  with:
    url: ${{ secrets.ROLLHOOK_URL }}
    token: ${{ secrets.ROLLHOOK_WEBHOOK_TOKEN }}
    image_tag: ghcr.io/you/my-api:${{ github.sha }}

2. Use image: ${IMAGE_TAG} and add a healthcheck

RollHook finds the compose file automatically from the running container's labels. No registration needed.

compose.yml
services:
  backend:
    # IMAGE_TAG is set by RollHook on each deploy.
    # The running container's image name is the discovery key —
    # RollHook finds it automatically via Docker Compose labels.
    image: ${IMAGE_TAG:-ghcr.io/you/my-api:latest}
    healthcheck:
      test: [CMD, curl, -f, http://localhost:3000/health]
      interval: 5s
      start_period: 10s
      retries: 5
    # No ports: — proxy routes via Docker DNS (backend:3000)
    # No container_name: — fixed names prevent the scaler from creating a second instance
    networks:
      - proxy

networks:
  proxy:
    external: true

What it handles

Webhook-Driven

POST /deploy from any CI pipeline. App derived from image_tag. Admin + webhook token roles.

Rolling Updates

TypeScript rolling deploy scales up, health-gates each container, then removes old ones.

Real-Time Logs

SSE stream delivers every log line to CI as it happens.

Job History

SQLite-backed, no external dependencies. Query status and logs any time.

Auto-Discovery

No config file. RollHook finds the compose file and service from Docker Compose labels on the running container.

Notifications

Pushover + configurable webhook on job completion.

What happens on deploy

  1. 01

    CI pushes image, sends POST /deploy

    Webhook body carries {"image_tag": "..."} — CI blocks until deployment completes. Returns HTTP 500 on failure.

  2. 02

    Discovers the running container automatically

    Matches the image name against running containers, reads Docker Compose labels to find the compose file path and service name. No config file needed.

  3. 03

    Compose file validated, image pulled

    Confirms the compose file exists on disk, then pulls the new image from your registry.

  4. 04

    Rolling deploy scales up, waits for health, removes old containers

    New container starts alongside the old one. Traffic continues to the old instance.

  5. 05

    Each step waits for healthcheck to pass

    Old container removed only after the new one passes its healthcheck. No traffic loss.

  6. 06

    Full log stream via SSE; Pushover fires on completion

    CI tails the log stream in real time. Notification sent with full job result on finish.

API Surface

POST  /deploy             # Trigger a deployment
GET   /jobs/:id           # Job status + metadata
GET   /jobs/:id/logs      # SSE log stream
GET   /jobs               # Deployment history (admin)
GET   /health             # Health check (no auth)
Deployment monitoring dashboard — coming soon
Multi-VPS support — coming soon

One GitHub Action away.

Add RollHook to your infra compose stack, add jkrumm/rollhook-action@v1 to your CI workflow, and you're done — no config file needed.

Read the docs on GitHub