Deploy Coolify on Docker: Build a Self-Hosted PaaS to Replace Heroku and Vercel in Your HomeLab

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

My phone buzzed at 2:13 AM with a billing alert from Vercel: a broken webhook retry loop had chewed through 140,000 serverless execution seconds, triggering an unexpected $180 tier upgrade. Staring at my screen with cold coffee in hand, I had enough. Paying commercial SaaS pricing to run experimental side projects and staging apps makes zero sense when you have idle compute sitting at home. That morning, I moved my entire app fleet to an old Dell OptiPlex in my home rack.

If you dread arbitrary usage quotas or surprise monthly invoices, Coolify is the fix. It is an open-source, self-hosted deployment engine that converts any Linux box into a modern Platform-as-a-Service (PaaS). Think of it as a private Heroku: you get push-to-deploy Git workflows, automated Let’s Encrypt certificates, point-and-click database provisioning, and pull-request preview environments—all hosted on your own hardware.

Quick start (5 min)

Spinning up Coolify takes less time than brewing fresh coffee. For hardware, an old mini PC with an Intel i5-8500, 16 GB of RAM, and a 256 GB NVMe drive will easily run dozens of lightweight services. Coolify itself requires a clean install of Ubuntu 22.04/24.04 LTS or Debian 12, at least 2 vCPU cores, 2 GB of memory, and root access.

First, update your package cache and install basic network utilities:

sudo apt update && sudo apt install -y curl ufw

Before running the installer, punch open the firewall ports. Coolify needs port 22 (SSH), port 80 (HTTP validation and redirects), port 443 (HTTPS traffic), and port 8000 for its primary web dashboard:

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8000/tcp
sudo ufw enable

Now pull and run the official bootstrap script. It identifies your CPU architecture (x86_64 or ARM64), installs Docker Engine and Compose plugins if missing, and boots up Coolify’s core containers:

curl -fsSL https://cdn.coolify.io/install.sh | bash

The script finishes in about two minutes. When the ASCII terminal banner appears, open your browser and hit your server’s local IP address:

http://192.168.1.150:8000

Create your primary admin account on the welcome screen. Your private cloud engine is now live and waiting for its first repository.

Deep dive: How Coolify orchestrates traffic

Portainer and Yacht act as basic graphical dashboards for raw Docker containers. Coolify works fundamentally differently. It operates as an automated CI/CD pipeline and traffic director, wiring an internal Traefik proxy straight into Docker’s event socket (/var/run/docker.sock).

Every time you trigger a new deployment, Coolify walks through a clean build cycle:

  • Build Detection: Coolify examines your repo root. If it finds a Dockerfile or a docker-compose.yml, it uses that directly. Otherwise, it invokes Nixpacks to parse runtime files like package.json, go.mod, or Cargo.toml, packaging your application into a lean, multi-stage production image.
  • Reverse Proxy Registration: Once the container fires up on an isolated Docker bridge network (such as coolify-infra), Traefik reads the container’s generated Docker labels. It begins proxying domain traffic immediately—no manual Nginx configs or daemon reloads needed.
  • SSL Handshake: Provided your Fully Qualified Domain Name (FQDN) points to your server’s public IP, Traefik triggers an ACME challenge and pulls a free Let’s Encrypt TLS certificate within 15 seconds.
  • State Storage: Coolify tracks your environment variables, build outputs, and team permissions inside an internal PostgreSQL container, persisting all data to the host directory at /data/coolify.

Mastering this pipeline demystifies container hosting. Once you understand how commits turn into routed network containers, you stop depending on proprietary platform guardrails.

Advanced usage

Running the dashboard is just stage one. The real payoff comes from setting up hands-off Git deployments and private databases.

Connecting Git Webhooks for Push-to-Deploy

To replicate the Vercel developer loop, link your GitHub or GitLab profile. Navigate to Sources > Add Source > GitHub App and follow the OAuth installation wizard. Set your webhook permissions, pick your repository, and select your deploy branch (like main or production).

Next, define production variables in the Environment Variables tab so secrets stay out of source code:

NODE_ENV=production
DATABASE_URL=postgresql://app_user:sUp3rS3cr3t@postgres-db:5432/production_app
PORT=3000

Every subsequent push to main builds a fresh container image, runs health checks, swaps the proxy pointer, and shuts down the old instance with zero downtime.

Spinning Up Private Databases with One Click

Avoid building database stacks by hand in multi-file Docker Compose setups. Coolify provisions PostgreSQL 16, Redis 7, MySQL 8, or MongoDB with a single click. Every database starts inside a private Docker network by default. Raw database ports never touch the public internet unless you deliberately enable external port forwarding for remote debugging.

Bypassing CGNAT with Cloudflare Tunnels

Most residential internet connections run on Carrier-Grade NAT (CGNAT) or rotating dynamic IPs. Opening ports 80 and 443 directly on your home router is also a security headache. The cleaner solution pairs Coolify with a lightweight Cloudflare Tunnel container. Point wildcard DNS records directly to Traefik’s internal ingress port:

# Example cloudflared config.yml snippet
ingress:
  - hostname: "*.lab.example.com"
    service: http://localhost:80
  - service: http_status:404

When you set an application domain like api.lab.example.com in Coolify, web traffic routes through Cloudflare’s edge network straight into your container over an outbound-only tunnel. Your home IP stays hidden, and you never have to configure router port forwarding.

Practical maintenance tips

Self-hosting means you are the site reliability engineer. Keep your PaaS healthy with these operational practices:

1. Schedule Weekly Docker Prunes to Avoid Disk Exhaustion

Continuous builds create untagged images, dangling build caches, and orphan volumes at an alarming rate. If your host root partition hits 100% capacity, Coolify’s PostgreSQL state database can lock up or corrupt its write-ahead log. Automate cache cleanup with a root cron job:

# Add to /etc/crontab: Purge unused build artifacts every Sunday at 3:00 AM
0 3 * * 0 root docker system prune -af --volumes

2. Automate Backups for /data/coolify

Every server definition, encrypted variable, and project schema lives inside /data/coolify. Snapshot this directory every night to a network share or an off-site S3 bucket using Rclone or Restic:

sudo rclone sync /data/coolify/ b2-storage:my-coolify-backups/ --fast-list

3. Enforce Hard Container Memory Limits

A single Node.js memory leak or runaway worker thread can consume host memory until the Linux Out-Of-Memory (OOM) killer kicks in. If the OOM killer panics and terminates Coolify’s orchestration engine or the Traefik proxy, your entire stack goes offline. Under your application’s Resources settings, assign a hard limit—like 512MB or 1024MB. Let individual offending containers crash and restart without endangering the rest of your server.

Coolify strikes the sweet spot between low-level Docker Compose files and costly managed cloud providers. By claiming ownership of your deployment stack, you get a dependable delivery pipeline that operates entirely on your terms.

Share: