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
- Provision — finds or creates a Hetzner
cx22(or your chosen type) - Prepare — SSHes in, installs Docker +
caddy-docker-proxyvia Docker Compose - DNS — creates the
Arecord automatically (Hetzner DNS) or prints it for manual entry, then polls until it resolves - Deploy — runs the container with Caddy labels; Caddy gets a Let's Encrypt cert automatically
- 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 | cx22 |
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. fordrawing.apps.example.comit triesapps.example.com, thenexample.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:
- Ensure VPS + prepare host — same as
redock up, butpreparealso runsdocker swarm init(idempotent) and ensuresredock_netis anoverlay --attachablenetwork so swarm services AND the Caddy container can both attach to it. - DNS — same automatic
Arecord creation as single-container deploys. - Deploy stack — uploads your YAML to
/opt/redock/stacks/<slug>.ymland runsdocker stack deploy -c <file> <slug>over SSH. Re-running updates the services in place (docker's native rolling update). - 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 uprequires 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.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| redock_vps-0.1.3.tar.gz | 96.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| redock_vps-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 155.4 kB
Release files / redock_vps-0.1.3.tar.gz
| Download URL | redock_vps-0.1.3.tar.gz |
|---|---|
| Size | 96.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c460efbdd970960d8b48de2c994a1afce837be7070ca5d67dda3be25b4d0a029
|
|
BLAKE2b-256 checksum How to use checksums |
a48cdf3deda3b2fd054ce7b5ed3bd31621847b414b94c54ffa5eaf6b4828f32c
|
| 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.3-py3-none-any.whl
| Download URL | redock_vps-0.1.3-py3-none-any.whl |
|---|---|
| Size | 59.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6068d4a70b7c6e6278a60375e9d3ae94a11fa8f7d74aee22de5952249ab3ebf6
|
|
BLAKE2b-256 checksum How to use checksums |
5c69321dcf4fb1036aa61cb26752e56ea7604c11a77240f369a6c46e685140a6
|
| 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}
|