What You'll End Up With
By the end of this guide you will have Coolify v4 running on a Hetzner CAX11 ARM instance (2 vCPU, 4 GB RAM, 40 GB NVMe, €3.79/month) with a Hetzner Floating IP attached for stable DNS, automatic SSL via Let's Encrypt, and the Coolify dashboard accessible at https://coolify.yourdomain.com. Total monthly cost including the Floating IP (€0.50) stays under $6. You will be able to deploy Node.js, Python, PHP, static sites, and Docker Compose stacks from a Git push — no Heroku, no Render, no platform fees.
Prerequisites:
- A Hetzner Cloud account (sign up at hetzner.com/cloud)
- A domain you control with access to DNS records
- SSH key already uploaded to Hetzner Cloud
- Local machine running macOS or Linux with
sshandcurlinstalled - Basic comfort with systemd and the Linux command line
1. Provision the CAX11 Instance
Log in to the Hetzner Cloud Console, open your project, and create a new server with these exact settings:
- Location: Falkenstein (FSN1) or Nuremberg (NBG1)
- Image: Ubuntu 24.04 (ARM64)
- Type: CAX11 (Ampere Altra, 2 vCPU, 4 GB RAM)
- Networking: Enable a public IPv4 via Floating IP (add it in the next step)
- SSH key: select the key you uploaded earlier
- Name:
coolify-prod
After the server starts, note the primary IPv4. You will replace it with the Floating IP shortly.
2. Attach a Floating IP
A Floating IP survives server rebuilds and lets you point DNS once.
In the Hetzner Cloud Console, go to Networking → Floating IPs → Create Floating IP. Choose IPv4, the same datacenter as your server, and assign it to coolify-prod.
SSH into the server using the primary IP first, then configure the Floating IP on the network interface:
ssh root@<PRIMARY_IP>
Create a netplan config for the Floating IP:
cat > /etc/netplan/60-floating-ip.yaml << 'EOF'
network:
version: 2
ethernets:
eth0:
addresses:
- <FLOATING_IP>/32
EOF
chmod 600 /etc/netplan/60-floating-ip.yaml
netplan apply
Verify the Floating IP is up:
ip addr show eth0 | grep '<FLOATING_IP>'
Expected output (truncated):
inet <FLOATING_IP>/32 scope global eth0
From this point forward, SSH using the Floating IP:
ssh root@<FLOATING_IP>
3. Point DNS to the Floating IP
At your DNS provider, create two A records:
| Name | Type | Value | TTL |
|---|---|---|---|
coolify |
A | <FLOATING_IP> |
300 |
*.coolify |
A | <FLOATING_IP> |
300 |
The wildcard record lets Coolify provision subdomains like myapp.coolify.yourdomain.com automatically.
Wait for propagation (TTL 300 = 5 minutes) before proceeding. Verify with:
dig +short coolify.yourdomain.com
Expected output:
<FLOATING_IP>
4. Harden the Server Before Installing Coolify
Do not skip this step on a public IP.
4a. Update all packages:
apt update && apt upgrade -y
4b. Install and configure UFW:
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment 'SSH'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw allow 8000/tcp comment 'Coolify dashboard'
ufw --force enable
Expected output:
Firewall is active and enabled on system startup
4c. Disable root password login (key-only):
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl reload ssh
4d. Set the hostname:
hostnamectl set-hostname coolify-prod
5. Install Docker Engine (ARM64)
Coolify requires Docker Engine 24+. Install from Docker's official repo — do not use the Ubuntu snap package.
apt install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify Docker is running:
docker version --format '{{.Server.Version}}'
Expected output (minimum):
26.1.4
Enable Docker on boot:
systemctl enable --now docker
6. Install Coolify v4
Coolify ships an official install script that sets up its own Docker Compose stack, a PostgreSQL database, Redis, and an Nginx reverse proxy — all in containers.
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
The script takes 2–4 minutes on CAX11. Watch the output; it ends with:
Coolify is ready to use!
Please visit http://<YOUR_IP>:8000 to get started.
Verify the containers are up:
docker ps --format 'table {{.Names}}\t{{.Status}}'
Expected output:
NAMES STATUS
coolify Up 2 minutes
coolify-db Up 2 minutes
coolify-redis Up 2 minutes
coolify-realtime Up 2 minutes
coolify-proxy Up 2 minutes
7. Configure the Domain and SSL in Coolify
Open http://<FLOATING_IP>:8000 in your browser. Complete the initial setup wizard:
- Create your admin account (email + password).
- On the Instance Settings page, set App URL to
https://coolify.yourdomain.com. - Under Proxy, confirm Traefik is selected (Coolify v4 default).
Next, tell Coolify to issue a TLS certificate for its own dashboard. In the Coolify UI:
- Go to Settings → Instance → SSL.
- Enable Let's Encrypt and enter your admin email.
- Save. Coolify restarts its Traefik container and issues the certificate.
After 30–60 seconds, visit https://coolify.yourdomain.com. You should see the Coolify login page over HTTPS with a valid certificate.
You can now close port 8000 in UFW since all traffic routes through Traefik on 443:
ufw delete allow 8000/tcp
ufw reload
8. Connect a Git Source and Deploy a Test App
This step proves the pipeline works end-to-end.
8a. In the Coolify dashboard, go to Sources → Add → GitHub (or GitLab/Gitea). Follow the OAuth flow to authorize Coolify to read your repositories.
8b. Go to Projects → New Project → New Resource → Application.
- Select your GitHub source.
- Pick any public Node.js or static site repo (e.g., a plain
index.htmlrepo). - Set the domain to
test.coolify.yourdomain.com. - Build pack: Static or Nixpacks (auto-detected).
- Click Deploy.
8c. Watch the build log in real time. A successful deploy ends with:
Container started successfully.
Visit https://test.coolify.yourdomain.com to confirm the app is live over HTTPS.
Verify It Works
Run these checks from your local machine:
# Dashboard reachable over HTTPS
curl -sI https://coolify.yourdomain.com | head -2
Expected:
HTTP/2 200
content-type: text/html
# TLS certificate valid and issued by Let's Encrypt
echo | openssl s_client -connect coolify.yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -issuer -dates
Expected (issuer line):
issuer=C=US, O=Let's Encrypt, CN=R11
# All Coolify containers healthy
ssh root@<FLOATING_IP> "docker ps --filter 'name=coolify' --format '{{.Names}} {{.Status}}'"
All containers should show Up X minutes with no Restarting entries.
Troubleshooting
Coolify install script fails with "Docker not found"
The script re-checks the PATH after installing Docker. If it still fails, run hash -r and re-execute the install script.
Port 8000 unreachable after install
Check UFW: ufw status numbered. Confirm rule 8000/tcp ALLOW IN exists. If you closed it early, re-add with ufw allow 8000/tcp temporarily.
Let's Encrypt certificate not issued
DNS must resolve before SSL issuance. Run dig +short coolify.yourdomain.com from an external machine. If it returns nothing, wait for propagation or lower TTL. Also confirm ports 80 and 443 are open in UFW and Hetzner's firewall rules.
Wildcard subdomain not routing to apps
Verify the *.coolify A record exists and matches the Floating IP. In Coolify's app settings, confirm the domain is set to the full subdomain, not just the app name.
Docker containers keep restarting
Run docker logs coolify --tail 50 to read the error. Most common cause on 4 GB RAM: the PostgreSQL container OOM-kills under load. Add a 2 GB swap file:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Coolify dashboard URL reverts to IP after restart The App URL setting is stored in the database. If you changed it before the DB was fully initialized, re-enter it at Settings → Instance → App URL and save again.
Next Steps
You now have a production-grade self-hosted PaaS on Hetzner for under $6/month. From here:
- Add a Hetzner Volume (€0.0476/GB/month) for persistent database storage outside the container layer — mount it at
/var/lib/coolify/volumes. - Enable automatic backups in Coolify's Settings to push database dumps to an S3-compatible bucket (Hetzner Object Storage starts at €0.0119/GB).
- Upgrade to CAX21 (4 vCPU, 8 GB RAM, €7.49/month) when you add more than 4–5 concurrent apps — the ARM instances give better price-per-core than x86 on Hetzner.
- Set up a Hetzner private network if you add a separate database server; keep DB traffic off the public internet entirely.
The Floating IP means you can rebuild or replace the underlying server at any time without changing DNS — a habit worth building before you need it at 2 AM.