Delivery is the path from a commit to a running process, and the interesting
question at every step is what happens when it fails.
The shape that works for most self-hosted applications is three stages: test,
build a versioned artifact, deploy that exact artifact. Building once and
promoting the same image is what makes the pipeline meaningful — if the deploy
rebuilds, the thing running is not the thing you tested.
Reach for a full pipeline as soon as more than one person deploys, or as soon as
you have deployed from a laptop at an inconvenient hour. Below that, a script in
the repository is genuinely fine and has fewer moving parts.
Three properties separate a pipeline that helps from one that provides false
confidence. Artifacts are tagged by commit, so rolling back is redeploying a tag
that already exists rather than rebuilding an old commit. The deploy step waits
for health rather than returning when a container is created. And there is one
real request against a real URL afterwards, because a container can be healthy
by its own definition and still be serving errors.
The security dimension is easy to underweight. A pipeline runs with access to
production credentials and is edited by anyone who can open a pull request.
Per-job permission scoping, environment secrets behind a required reviewer, and
third-party actions pinned to a commit rather than a mutable tag are the three
controls that matter most, and none of them cost anything.
Test, build a container image, push it to GHCR and deploy it to a VPS over SSH — with the concurrency guard, environment approval and token scoping that keep the pipeline from becoming the weak point.
Intermediate · 4 min read
Beginner to advanced
A reading order that builds DevOps knowledge in the sequence it is actually needed.
Test, build a container image, push it to GHCR and deploy it to a VPS over SSH — with the concurrency guard, environment approval and token scoping that keep the pipeline from becoming the weak point.
Dokploy runs apps as Docker Swarm services, so rolling updates are already there — they just need a real health check and an update policy. Plus how to prove no request was dropped.
A nightly backup for a self-hosted Docker host: database dumps taken with the right tools, file volumes snapshotted with restic, a systemd timer to run it, and a restore test that proves it works.
A nightly backup for a self-hosted Docker host: database dumps taken with the right tools, file volumes snapshotted with restic, a systemd timer to run it, and a restore test that proves it works.
4 min
AI-assisted
Frequently asked questions
Should the container image be built in CI or on the server?
In CI. Building on the target consumes the resources your application needs and means the artifact that ships was never tested — you tested a different build of the same commit.
What is the minimum viable deployment safety net?
Tag images by commit SHA, wait for health checks before declaring success, run one smoke request against a real URL, and know the command that redeploys the previous tag. Four things, none of them large.
How much CI is too much for a small team?
When the pipeline takes longer than the change did, people stop running it locally and start pushing to see what happens. Keep the fast checks fast and move anything slow to a nightly job.