Skip to main content

redock

One command from zero to a live HTTPS app on a Hetzner VPS.

redock up excalidraw --slug drawing

That's it. redock provisions a server, installs Docker and a Caddy reverse proxy, handles DNS, deploys the container, and hands you back a live URL.

How it works

  1. Provision — finds or creates a Hetzner cx23 (or your chosen type)
  2. Prepare — SSHes in, installs Docker + caddy-docker-proxy via Docker Compose
  3. DNS — creates the A record automatically (Hetzner DNS) or prints it for manual entry, then polls until it resolves
  4. Deploy — runs the container with Caddy labels; Caddy gets a Let's Encrypt cert automatically
  5. Verify — polls HTTPS until the app responds 200, then prints the URL

The VPS is shared across deployments — each app is one container behind Caddy.

Prerequisites

  • A Hetzner Cloud account and API token
  • An SSH key uploaded to Hetzner (default name: redock-key)
  • A domain you can add DNS records to
  • Python 3.11+ and uv

For automatic DNS (optional): a Hetzner DNS account with a zone for your domain.

Install

uv pip install redock

Or from source:

git clone https://gitlab.com/toopy/redock
cd redock
uv sync

Configuration

Copy .env.example to .env and fill in the required values:

cp .env.example .env
Variable Required Default Description
REDOCK_HETZNER_TOKEN yes — Hetzner Cloud API token
REDOCK_BASE_DOMAIN yes — Domain for deployments, e.g. apps.example.com
REDOCK_ACME_EMAIL yes — Email for Let's Encrypt notifications
REDOCK_HETZNER_SSH_KEY_NAME no redock-key SSH key name in Hetzner Cloud
REDOCK_SSH_KEY_PATH no ~/.ssh/id_ed25519 Path to local private key
REDOCK_HETZNER_REGION no fsn1 Hetzner datacenter
REDOCK_HETZNER_SERVER_TYPE no cx23 Server type
REDOCK_STATE_FILE no ~/.redock/state.json Where deployment state is stored
REDOCK_CATALOG_DB no ~/.redock/catalog.db Catalog SQLite database path
REDOCK_INFOMANIAK_API_KEY no — Required for catalog update --use-ai
REDOCK_INFOMANIAK_PRODUCT_ID no — Required for catalog update --use-ai
REDOCK_INFOMANIAK_CHAT_MODEL no google/gemma-4-31B-it LLM model for metadata extraction
REDOCK_DOCKERHUB_USERNAME no — Docker Hub username for private image metadata
REDOCK_DOCKERHUB_TOKEN no — Docker Hub access token (read-only) for private image metadata
REDOCK_SCAN_TRIVY_WEIGHT no 0.5 Trivy's share of the combined scan score
REDOCK_SCAN_SCOUT_WEIGHT no 0.5 Docker Scout's share of the combined scan score
REDOCK_SCAN_SEV_CRITICAL no 5 Penalty coefficient per CRITICAL vulnerability
REDOCK_SCAN_SEV_HIGH no 2 Penalty coefficient per HIGH vulnerability
REDOCK_SCAN_SEV_MEDIUM no 0.5 Penalty coefficient per MEDIUM vulnerability
REDOCK_SCAN_SEV_LOW no 0.1 Penalty coefficient per LOW vulnerability

Then verify everything looks good:

redock doctor

Automatic DNS with Hetzner DNS

redock uses REDOCK_HETZNER_TOKEN for both VPS provisioning and DNS management. If your domain zone exists in console.hetzner.com → Networking → DNS, redock will create or update the A record automatically on every redock up.

Setup: Add your domain zone in the Hetzner Console and point your registrar's nameservers to Hetzner's — no extra token needed.

Note: redock uses the Hetzner Cloud DNS API (api.hetzner.cloud/v1). The zone must already exist — redock manages records within a zone, not the zone itself. It discovers the right zone by walking the hostname labels (e.g. for drawing.apps.example.com it tries apps.example.com, then example.com).

A real example

Deploy Excalidraw as drawing.apps.example.com:

redock up excalidraw --slug drawing
● Ensuring VPS
● Preparing host (Docker + Caddy)
● DNS records — creating and waiting for propagation
✓ Created DNS A record: drawing.apps.example.com → 65.21.104.33
● Deploying container
● Waiting for HTTPS
✓ Live: https://drawing.apps.example.com

Done! https://drawing.apps.example.com

The --slug flag sets the subdomain prefix. If omitted it defaults to the template name:

redock up <id>                          # → <id>.<base_domain>
redock up <id> -s <slug>               # → <slug>.<base_domain>
redock up <id> --env KEY=VALUE         # override an env var at deploy time
redock up <id> --upgrade               # stop existing container, pull latest image, redeploy

Multi-container apps — docker swarm stacks

For apps that ship as more than one container (a service + a worker, an API + a frontend, anything you'd model as a docker-compose.yml), redock can deploy the whole stack via docker swarm. The same VPS hosts multiple stacks side-by-side, all behind the shared Caddy.

redock stack up ./stack.yml --slug myapp
# → https://myapp.<base_domain>

What it does:

  1. Ensure VPS + prepare host — same as redock up, but prepare also runs docker swarm init (idempotent) and ensures redock_net is an overlay --attachable network so swarm services AND the Caddy container can both attach to it.
  2. DNS — same automatic A record creation as single-container deploys.
  3. Deploy stack — uploads your YAML to /opt/redock/stacks/<slug>.yml and runs docker stack deploy -c <file> <slug> over SSH. Re-running updates the services in place (docker's native rolling update).
  4. Wait HTTPS — polls until the public URL responds.

Stack file requirements

Your stack.yml is a normal docker compose v3+ file with two additions:

  • The publicly-reachable service must declare the Caddy labels so caddy-docker-proxy routes the hostname to it:

    services:
      web:
        image: yourorg/web:latest
        networks:
          - redock_net
        deploy:
          labels:
            - "caddy=$DOMAIN"
            - "caddy.reverse_proxy={{upstreams 8080}}"
    
  • All services that must be on the Caddy network reference the external attachable overlay:

    networks:
      redock_net:
        external: true
    

$DOMAIN (and any other ${VAR}) get interpolated from --env KEY=VAL at deploy time. redock substitutes them via the shell so compose's native interpolation picks them up. See swarm-me's stack.yml for a real 4-service example.

Stack commands

redock stack up ./stack.yml --slug myapp           # deploy / update
redock stack up ./stack.yml --slug myapp \
  --env DOMAIN=myapp.deploy-me.eu                  # env substitution
redock stack up ./stack.yml --slug myapp \
  --registry-auth 'registry.gitlab.com::ci-token::glpat-xxx'  # private registry pull
redock stack rm myapp                              # take it down (keeps VPS)
redock stack list                                  # what's running on the shared VPS

Stacks coexist with single-container redock up deployments on the same VPS — Caddy reads labels from both swarm service tasks and plain containers via the shared redock_net overlay.

App catalog

# Add an app (all flags optional; Sub-project 2 will fill them via AI)
redock catalog add nginx:latest --port 80
redock catalog add louislam/uptime-kuma:latest --port 3001 --volume /app/data

# Store environment variables on the catalog item (repeatable)
redock catalog add myapp:latest --port 8080 --env DB_URL=postgres://localhost/db --env DEBUG=false
redock catalog update myapp --env SECRET_KEY=abc123   # merges with existing env vars

# Reference a host env var — prefix value with $ (use single quotes to prevent shell expansion)
redock catalog update myapp --env 'DB_PASS=$MY_SECRET'   # stored as-is; resolved at deploy time

# Mark as ready to deploy (or uncheck with --no)
redock catalog check nginx
redock catalog check --no nginx

# List, inspect, update, remove
redock catalog list
redock catalog list --checked-only
redock catalog show nginx
redock catalog update nginx --port 8080
redock catalog delete nginx

# Search the local catalog
redock catalog search ngi

# Scan an image for vulnerabilities (Trivy + Docker Scout)
redock catalog scan nginx
redock catalog scan nginx --dry-run   # scan without saving results
redock catalog scan nginx --verbose   # show per-scanner breakdown

Security scanning

redock catalog scan runs Trivy and Docker Scout against the image, merges their findings, and computes a weighted 0–100 safety score. At least one scanner must be installed — the command skips unavailable scanners gracefully and only fails if none can run.

Install Trivy

task trivy:install

This runs the official install script and places trivy in ~/.local/bin (no sudo needed). Alternatively, install manually:

  • macOS: brew install aquasecurity/trivy/trivy
  • Linux: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b ~/.local/bin

Verify: trivy --version

Install Docker Scout

task scout:install

This installs the Docker Scout CLI plugin to ~/.docker/cli-plugins. Docker Desktop 4.17+ already bundles it — run docker scout version to check before installing.

Verify: docker scout version

Note: redock up requires the template to exist in the catalog and be marked as checked. A missing port also blocks deployment.

Other commands

# See all running deployments
redock list

# Stop a deployment (keeps container and state, can be restarted)
redock stop <slug>

# Remove container + volume + state + DNS record, destroy VPS if no apps remain
redock purge <slug>
redock purge <slug> --yes  # skip confirmation

# VPS - Step-by-step control
redock vps create         # provision VPS only
redock vps install        # install Docker + Caddy only
redock vps show           # print VPS IP and status
redock vps delete         # destroy VPS (blocked if deployments still exist)

# DNS - Step-by-step control
redock dns show <slug>    # print required DNS record
redock dns update <slug>  # create/update A record (Hetzner DNS)
redock dns wait <slug>    # wait until DNS resolves
redock dns clean <slug>   # delete A record (Hetzner DNS)

License

MIT

Metadata

Release files for redock-vps 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for redock-vps 0.1.4
File Size Uploaded
redock_vps-0.1.4.tar.gz 96.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for redock-vps 0.1.4
File Interpreter ABI Platform
redock_vps-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 156.0 kB

Release files / redock_vps-0.1.4.tar.gz

Download URL redock_vps-0.1.4.tar.gz
Size 96.4 kB
Tags Source
SHA-256 checksum
How to use checksums
aad3c39dd5bb97cfd6b62f3afc972014f000705b0423e7f16d4a744654b963bf
BLAKE2b-256 checksum
How to use checksums
27fb41aa89dc0d550e1c225976670542a96d0133aafdbf1b833c0e8a26a45a66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / redock_vps-0.1.4-py3-none-any.whl

Download URL redock_vps-0.1.4-py3-none-any.whl
Size 59.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6053a9c84a74d3f95ebe69d396f2c056218a91b7366703413252bb3fcf0dc147
BLAKE2b-256 checksum
How to use checksums
d3070a68ecefdb968658b2470489b86e8e61f545f59898a32660bcdc1b867792
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page