Quickstart
From a Linux server to a first zero-downtime deploy, a second deploy and a rollback.
This guide deploys a small web app to one server: an nginx container, served over HTTPS. You will:
- install the CLI;
- prepare the server with one command;
- deploy the app, deploy a change, and roll it back.
It takes about ten minutes.
-
Check the prerequisites.
You need:
- a Linux server (
amd64orarm64), new or already in use. Dockflow installs Docker on Debian and Ubuntu. On another distribution, install Docker Engine 27 or later first; - SSH access to it as
rootor as a user with passwordlesssudo, or a root shell on it; - ports 80 and 443 of the server free and reachable from the internet;
- a domain name whose DNS record points to the server. This guide uses
hello.example.com.
The full list is in server requirements. If an nginx on the server already holds ports 80 and 443, follow behind nginx for the server part.
- a Linux server (
-
Install the CLI on your machine.
curl -fsSL https://raw.githubusercontent.com/Shawiizz/dockflow/main/install.sh | sh dockflow versionHomebrew, npm, Windows and other options are in installation.
-
Prepare the server. Run
server initfrom your machine or on the server itself. The result is the same.dockflow server init [email protected]Dockflow connects with your SSH key, from
ssh-agent,~/.sshor-k <key>. It generates the deploy key on your machine, so the private key never reaches the server.connecting to 203.0.113.10:22 as root installing Docker from download.docker.com (debian bookworm) creating the user dockflow adding dockflow to the docker group installing the deploy key of dockflow connecting as dockflow with the deploy key installing the agent image dockflow/agent:1.0.0-alpha.6 ✓ 203.0.113.10 is ready (edge: builtin, deploy user: dockflow) The connection string below holds the deploy key: store it as the <ENV>_CONNECTION secret … eyJ2IjoyLCJzZXJ2ZXJzIjpb…server init:- installs Docker when it is missing, from Docker's own repository;
- creates the deploy user
dockflow, in thedockergroup, with an SSH key restricted to what Dockflow uses. The private key is never written on the server; - reconnects as
dockflowand installs the switch (dockflow-switch, a Caddy container that publishes ports 80 and 443 and obtains certificates) and the server-local registry (dockflow-registry, on127.0.0.1:5959); - prints the connection string: the server, its host key and the deploy key, in one value.
It leaves SSH's settings alone. Add
--harden-sshto turn passwords off and let root in by key only, once your account has a key.You can run it again at any time. On a prepared server, it changes nothing.
The host key
From your machine, Dockflow never trusts a server it cannot verify. If you have already connected to the server with
ssh, its key is in your~/.ssh/known_hostsand there is nothing to do.Otherwise,
server initstops and shows the server's fingerprint. Compare it with the output ofssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pubon the server, or with your provider's console. Then run it again with--host-key SHA256:….Create a directory for the project. Store the connection string in a local
.env.dockflowfile, kept out of git. In CI, it becomes thePRODUCTION_CONNECTIONsecret (see deploying from CI)..env.dockflow PRODUCTION_CONNECTION=eyJ2IjoyLCJzZXJ2ZXJzIjpb…echo .env.dockflow >> .gitignoreThe connection string contains the deploy key. Anyone who holds it controls Docker on the server, which means root. Never commit
.env.dockflow. If it leaks, runserver initagain, which installs a new key, and remove the old line from/home/dockflow/.ssh/authorized_keys. -
Describe the environment.
Write
dockflow.ymlat the root of the project. The server comes from the connection string, so the file does not mention it:dockflow.yml version: 1 app: hello environments: production: vars: DOMAIN: hello.example.com -
Describe the app.
Write
compose.ymlnext todockflow.yml. It is a standard Compose file. Thex-dockflowkey, which Compose ignores, adds a route.compose.yml services: web: image: nginx:1.27-alpine healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/"] interval: 5s timeout: 3s retries: 3 x-dockflow: routes: - domains: ["${DOMAIN}"] port: 80${DOMAIN}takes its value from the environment'svars.- The route sends
https://hello.example.comto port 80 of the container. - The healthcheck tells Dockflow when a new container is ready for traffic.
dockflow init --domain hello.example.comwrites these two files for you. With--ci githubor--ci gitlab, it also writes a CI workflow (seeinit).Check the project without touching the server:
dockflow validate productionproduction: valid, 1 services -
Look at the plan.
dockflow plan productionPlan: 1 to create, 0 to update, 0 to remove · network.create hello-production_default: does not exist + service.create web [switch]: new service · routes.apply hello-production: routes changedThe plan is computed from what the server actually runs. Nothing is changed yet.
-
Deploy.
dockflow deploy productionOn a terminal,
deployshows the same plan and asks for confirmation.-yskips the question, and CI never asks.Then the agent, a short-lived container on the server, starts
web, waits for its healthcheck, points the route at it, and records release 1. The switch obtains a certificate forhello.example.comfrom Let's Encrypt on its own.curl https://hello.example.com/Since
dockflow.ymlhas a single environment, you can leave outproductionin every command. -
Check the status.
dockflow status productionIt shows the current release, its version label and the containers of each service. The version label is the short git commit, or the time of the deploy outside a git repository.
dockflow pslists the containers, anddockflow logs webshows their logs. -
Change something and deploy again.
Change the image tag in
compose.yml:compose.yml services: web: image: nginx:1.28-alpinedockflow deploy productionPlan: 0 to create, 1 to update, 0 to remove ~ service.update web [switch]: image nginx@sha256:… → nginx@sha256:…The new container starts next to the old one. Once it is healthy, traffic moves to it. The old container gets 30 seconds to finish the requests in flight, then stops. No request fails during the switch.
Deploying again without a change prints
Nothing to change.and creates no release. -
Roll back.
dockflow rollback productionThis restores the previous release, with the same zero-downtime switch. It uses the image still on the server, so nothing is rebuilt or downloaded. The rollback is itself a new release:
dockflow releases productionSEQ VERSION STATUS CREATED BY 3 a1b2c3d active (rollback to 1) 2026-10-26T14:12:40Z alice ← current 2 e4f5a6b superseded 2026-10-26T14:08:02Z alice 1 a1b2c3d superseded 2026-10-26T14:03:11Z alicedockflow rollback --to 2would restore release 2.
If a deploy fails, for example because the new container never becomes healthy, Dockflow switches back to the previous containers on its own and exits with code 8. See releases and rollbacks.