One-command Docker deployment to Hetzner VPS
Project description
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
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file redock_vps-0.1.1.tar.gz.
File metadata
- Download URL: redock_vps-0.1.1.tar.gz
- Upload date:
- Size: 90.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca08249a575d78a4e0afb2c5fbd46fa4d5171cf71330e0b3461e4e5080c77c42
|
|
| MD5 |
ca544da59d49085980a7cd78180a882a
|
|
| BLAKE2b-256 |
472b9bb0ca445846cf4172ec0a57c0a321d05e2ea85c8d08acda23383c9767de
|
File details
Details for the file redock_vps-0.1.1-py3-none-any.whl.
File metadata
- Download URL: redock_vps-0.1.1-py3-none-any.whl
- Upload date:
- Size: 51.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f859bed0aa52198b02a1d780a4d8647723bd3d34bfa278fbf1907a8e3bb93f37
|
|
| MD5 |
b050fa85747e033f8096bc5f67f540a8
|
|
| BLAKE2b-256 |
5636cda5ac6bd9c025ef034d8727580238e0d71a40b5aef182afb7af9289b356
|