This post is part of a tiny backend platform series:
- Minimal Dokku Setup for Side Projects
- Cloudflare proxy for my Dokku server
- Adding a persistence layer for side projects
Why This Is Hard
Every now and then I get back to tinkering with side projects. As a mobile developer I just need the tiniest backend: return some JSON, write a couple of rows to a database, run a simple bit of logic. Nothing heroic. But the moment I open the first Google result and try to follow the “standard” approach, the supposed basic backend turns into a quest.
Usual Options
I genuinely like Cloudflare Workers, but their SDK immediately dictates an edge architecture. Minimal containers on DigitalOcean quickly turn into a noticeable bill, and Fly.io (plus similar platforms) comes with its own constraints. There is also Coolify: it converts your server into a mini-cloud and covers plenty of corner cases, but you pay for that with the complexity of the solution itself. You need a beefy server, so the infrastructure cost climbs again and suddenly looks like something for a small business, not a Sunday experiment.
I Want Minimalism
I want something so cheap I can forget about it for a year; without quirky limits so any quick-start guide from the internet works as-is; and flexible enough to wrap a backend without thinking about the stack—Python today, JavaScript tomorrow, Swift the day after. Adding another half-dead project shouldn’t bump the bill or break the setup: services should just run side by side without drama.
The Sweet Spot: Hetzner + Dokku + Dockerfile
- Hetzner/VPS: low-cost, reliable, and always on.
- Containers: the
Dockerfileabstracts the stack, so Python, JavaScript, and Swift deploy the same way. - Dokku: a lightweight “manager” for containers on the box—configure once, spin up or update tiny apps with a couple of simple commands.
- Budget: roughly €5/month plus a domain (~€1.5/year) to get human-friendly URLs and enable HTTPS.
Step-by-Step
Here’s how I set up and use the Hetzner + Dokku + Dockerfile combo. It takes about an hour and leaves you with your own mini-cloud.
Renting the VPS
The provider doesn’t really matter—we just need a small Linux box with SSH access. I’ll show it with Hetzner because the price/performance ratio is excellent.
By the end of this step you should have:
- a running Linux server
- a public IP address
- a password or SSH key so you can log in with
ssh root@<ip>
Hetzner Cloud server
- Open the cloud console and create a project if you don’t have one already.
- Inside the project hit Create server and pick a plan. I use
CX23on x86. There’s alsoCAX11on ARM—much faster for the same money—but not all software loves ARM, so for a “no-stress” setup I stay on x86. - Location: I choose Germany, Falkenstein. Pick whatever region is closest to you (latency checker).
- OS: Ubuntu LTS has the most straightforward guides. If you crave minimalism and don’t mind googling a bit, grab Debian for slightly better efficiency.
- Networking: I add an IPv4 address (+€0.61/month for me) because my ISP still doesn’t do IPv6. You might be able to skip it.
- SSH: upload your public key (or create a new one) to log in without a password—it makes Dokku setup easier.
- Skip the remaining toggles (backup, extra disks). You can revisit them later.
- Hit Create & Buy now, wait for the server to boot, and confirm access via
ssh root@<ip> -i ~/.ssh/hetzner(if SSH complains about permissions, runchmod 600 ~/.ssh/hetzner).
Expect to pay around €3.5–4.5/month for this configuration.
Domain Setup
I buy the cheapest domain I can find—zones like .xyz usually cost €1–2/year—and configure DNS right at the registrar. If they don’t provide DNS, I hook up free Cloudflare DNS.
- Open the domain management panel.
- Create a wildcard record
*.chenchik.me(A/AAAA) pointing to the server’s IP.
Now every subdomain such as helloworld.chenchik.me resolves to my VPS.
Installing Dokku
The rest of the commands happen on the server: ssh root@<ip> -i ~/.ssh/hetzner.
- (Optional) update the system:I usually reboot afterward (
# Ubuntu update all sudo -s -- <<'EOF' apt-get update apt-get upgrade -y apt-get full-upgrade -y apt-get autoremove -y apt-get autoclean -y EOFsudo reboot) and reconnect. - Install Dokku. Latest version at the time of writing is 0.36.10, so I pull the official script and pin the tag:
wget -NP . https://dokku.com/install/v0.36.10/bootstrap.sh sudo DOKKU_TAG=v0.36.10 bash bootstrap.sh - Add an SSH key for deployments by reusing the key I already use for the server:Better yet, generate a dedicated key pair for deployments. If you have a public key elsewhere, just pass it directly:
cat ~/.ssh/authorized_keys | dokku ssh-keys:add adminecho '<your-public-key>' | dokku ssh-keys:add admin - Set the default domain:
dokku domains:set-global <your-domain> - Install the Let’s Encrypt plugin, wire up your email, and enable automatic renewals:
sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git dokku letsencrypt:set --global email <your-email> sudo dokku letsencrypt:cron-job --add
That’s it—Dokku is ready and we can move on to the first backend.
First Backend
Time to deploy something. The first two commands run on the server; everything else happens locally.
- Create an app on the server:
dokku apps:create helloworld - Enable HTTPS:
dokku letsencrypt:enable helloworld - Locally, create an empty Git repo and point it at Dokku:
git init helloworld cd helloworld git remote add dokku dokku@<ip>:helloworld - Verify the connection (
ssh -T dokku@<ip>should list commands). If it fails, add a snippet to~/.ssh/configso SSH knows which key to use:
After creating the file, runHost <ip> IdentityFile ~/.ssh/hetzner AddKeysToAgent yes UseKeychain yeschmod 600 ~/.ssh/configto fix permissions. - Create a minimal backend:
cat <<'EOF' > server.js const http = require('http'); http.createServer((_, res) => { res.writeHead(200, {'Content-Type':'application/json'}); res.end('{"message":"hello world"}'); }).listen(80); EOF - Add a tiny
Dockerfile:cat <<'EOF' > Dockerfile FROM node:24-alpine COPY server.js . EXPOSE 80 CMD ["node", "server.js"] EOF - Commit and deploy. As soon as
git pushreaches the server, Dokku builds the image and restarts the app:git add -A && git commit -m "first backend" git push --set-upstream dokku main
Done! The app now lives at https://helloworld.chenchik.me
