Intro
The time to migrate has come? This thing has been sitting on my to-do list for months as I keep stalling and procrastinating with more important work. I have started migrating some stuff, not everything yet, this blog still remains but I will do it in the upcoming period. I wanted more control over my infrastructure and to learn the ins and outs of self-hosting. After some research and a weekend of tinkering, I found my answer: Hetzner for the infrastructure and Coolify as the self-hosted deployment platform.
In this guide, I’ll walk you through exactly how I made the migration, the gotchas I encountered, and everything you need to know to make the switch yourself
The Stack
Here’s what we’ll be working with:
- Hetzner Cloud: German cloud provider with excellent prices and performance
- Coolify: Open-source, self-hostable deployment platform
- Docker: For containerization (Coolify handles this mostly)
- Cloudflare: For DNS and optional CDN (free tier)
Step 1: Setting Up Your Hetzner Server
First things first, we need a server to work with.
Creating a Hetzner Account
Head over to hetzner.com and create an account. You’ll need to verify your identity (usually with a credit card or PayPal), but the process is straightforward.
Launching Your First Server
Once you’re in the Hetzner Cloud Console:
- Click “New Project” and give it a name
- Click “Add Server”
- Choose your location (I went with Nuremberg for EU, but pick what’s closest to your users)
- Select Ubuntu 22.04 as your image
- For server type, I recommend starting with CPX21 (3 vCPU, 4GB RAM, 80GB disk) which costs around €8/month
- Add your SSH key (we’ll need this for secure access)
Generating an SSH Key (if you don’t have one):
On your local machine, open terminal and run:
ssh-keygen -t ed25519 -C "[email protected]"
Press Enter to accept the default location, and optionally set a passphrase. Then display your public key:
cat ~/.ssh/id_ed25519.pub
Copy this and paste it into Hetzner’s SSH key field.
- Leave the other options as default and click “Create & Buy Now”
Your server will be ready in about a minute. Note down the IP address that appears.
Initial Server Setup
First, let’s connect to our new server:
ssh root@your_server_ip
Update the system packages:
apt update && apt upgrade -y
Create a new user (never run everything as root):
adduser yourusername
usermod -aG sudo yourusername
Copy your SSH key to the new user:
rsync --archive --chown=yourusername:yourusername ~/.ssh /home/yourusername
Now exit and reconnect as your new user:
exit
ssh yourusername@your_server_ip
Step 2: Installing Coolify
This is where the magic happens. Coolify makes self-hosting feel almost as easy as Vercel.
Prerequisites Check
Coolify requires a clean Ubuntu server with at least 2GB RAM. Make sure your server meets these requirements:
free -h # Check RAM
df -h # Check disk space
Installation
The installation is surprisingly simple. Run this single command:
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
This script will:
- Install Docker and Docker Compose
- Set up Coolify and all its dependencies
- Configure the firewall
- Start all necessary services
The installation takes about 5-10 minutes. Grab a coffee while it runs.
Once complete, you’ll see a message with your Coolify URL:
Coolify is now available at http://your_server_ip:8000
First-Time Coolify Setup
Open your browser and navigate to http://your_server_ip:8000.
- You’ll be greeted with a registration screen
- Create your admin account (save these credentials somewhere safe)
- You’ll land on the dashboard
The first thing Coolify will ask you to do is set up your SSH key for deployments. It will generate one automatically, which is fine for most use cases.
Step 3: Configuring Your Domain
Before deploying anything, let’s set up a proper domain with SSL.
DNS Configuration
Head to your domain registrar (or Cloudflare if you’re using them for DNS) and add these records:
A Record:
- Type: A
- Name: @ (or your subdomain)
- Value: your_server_ip
- TTL: Auto or 3600
Wildcard A Record (optional but recommended):
- Type: A
- Name: *
- Value: your_server_ip
- TTL: Auto or 3600
This wildcard record lets you easily create subdomains for different projects.
Wait a few minutes for DNS propagation (usually 5-15 minutes).
SSL Setup in Coolify
Coolify uses Let’s Encrypt for automatic SSL certificates, but we need to configure it first.
- In Coolify, go to Settings → Configuration
- Find “Instance Settings”
- Update your Coolify URL to use your domain:
https://coolify.yourdomain.com - Enable “Automatic SSL” (this uses Let’s Encrypt)
- Save settings
Coolify will automatically obtain and renew SSL certificates for all your deployments.
Step 4: Deploying Your First Application
Let’s deploy a Next.js app (since that’s what most people are migrating from Vercel).
Preparing Your Repository
Make sure your Next.js project has these files:
Dockerfile (if not already present):
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
next.config.js (add this if using standalone output):
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfig
Push these changes to your Git repository.
Creating the Application in Coolify
- In Coolify dashboard, click ”+ New Resource”
- Select “Application”
- Choose “Public Repository” (or connect your GitHub/GitLab account)
- Paste your repository URL
- Select the branch you want to deploy (usually
mainormaster) - Coolify will auto-detect it’s a Next.js app
Configure the deployment:
Build Settings:
- Build Pack: Dockerfile (or Nixpacks if you don’t have a Dockerfile)
- Build Command:
npm run build(if using Nixpacks) - Start Command:
npm start(if using Nixpacks)
Domain Settings:
- Add your domain:
app.yourdomain.com - Enable “Generate SSL”
Environment Variables:
- Click “Add” to add any environment variables your app needs
- These are the same ones you had in Vercel
Click “Deploy” and watch the magic happen in the deployment logs.
Monitoring the Deployment
Coolify provides real-time logs during deployment. You’ll see:
- Git repository cloning
- Docker image building
- Container starting
- SSL certificate generation
First deployment usually takes 3-5 minutes. Subsequent deployments are faster due to caching.
Step 5: Setting Up Automatic Deployments
Just like Vercel, we want deployments to trigger automatically when we push to our repository.
GitHub Integration
- In Coolify, go to your application settings
- Find “Webhooks” section
- Copy the webhook URL provided
- Go to your GitHub repository → Settings → Webhooks
- Click “Add webhook”
- Paste the Coolify webhook URL
- Select “Just the push event”
- Make sure “Active” is checked
- Click “Add webhook”
Now every time you push to your repository, Coolify will automatically deploy the changes.
GitLab/Bitbucket
The process is similar:
GitLab:
- Repository → Settings → Webhooks
- Add the Coolify webhook URL
- Select “Push events”
Bitbucket:
- Repository Settings → Webhooks
- Add webhook with Coolify URL
- Select “Repository push” trigger
Step 6: Database Setup
If your application needs a database, Coolify makes this incredibly easy.
PostgreSQL Example
- In Coolify, click ”+ New Resource”
- Select “Database”
- Choose “PostgreSQL”
- Configure:
- Name:
myapp-db - PostgreSQL version: 15 (or your preferred version)
- Database name, username, and password
- Name:
- Click “Deploy”
Coolify will:
- Deploy PostgreSQL in a Docker container
- Set up persistent volumes
- Provide connection details
Connecting Your App to the Database
In your application’s environment variables, add:
DATABASE_URL=postgresql://username:password@myapp-db:5432/database_name
Note that myapp-db is the internal Docker network name. Coolify handles the networking automatically.
Other Databases
Coolify supports:
- MySQL/MariaDB
- MongoDB
- Redis
- MinIO (S3-compatible storage)
The setup process is identical for all of them.
Step 7: Advanced Configuration
Environment-Specific Settings
You can create multiple environments for the same project:
- Clone your production application in Coolify
- Point it to a different branch (e.g.,
staging) - Use a different domain (e.g.,
staging.yourdomain.com) - Configure different environment variables
Custom Build Commands
If your project has special build requirements:
- Go to application settings
- Find “Build & Deploy” section
- Add custom commands:
- Pre-build commands
- Build commands
- Post-build commands
- Start commands
Example for a monorepo:
# Pre-build
cd packages/frontend
# Build
npm run build
# Start
npm start
Health Checks
Set up health checks to ensure your application is running properly:
- Application settings → Health Checks
- Configure:
- Health check path:
/api/health(or your health endpoint) - Interval: 30 seconds
- Timeout: 10 seconds
- Retries: 3
- Health check path:
Coolify will automatically restart your container if health checks fail.
Resource Limits
Control how much CPU and memory your application can use:
- Application settings → Resources
- Set:
- Memory limit: 512MB, 1GB, 2GB, etc.
- CPU limit: 0.5, 1, 2 cores, etc.
This prevents one application from consuming all server resources.
Common Issues and Solutions
During my migration, I encountered a few hiccups. Here’s how I solved them:
Issue 1: Build Fails with “Out of Memory”
Solution: Increase the memory limit for your application, or upgrade your server. Build processes (especially for large Next.js apps) can be memory-intensive.
Temporary fix during build:
# In Coolify, add this to pre-build commands
export NODE_OPTIONS="--max-old-space-size=2048"
Issue 2: Application Not Accessible After Deployment
Solution: Check these in order:
- Verify DNS records are pointing to the correct IP
- Check if the container is actually running:
docker ps - Review deployment logs in Coolify
- Ensure the correct port is exposed in your Dockerfile
- Check firewall rules:
sudo ufw status
Issue 3: SSL Certificate Not Generating
Solution:
- Make sure DNS is fully propagated (wait 15-30 minutes)
- Verify port 80 and 443 are open
- Check Let’s Encrypt rate limits (you might have hit them)
- Try regenerating the certificate manually in Coolify settings
Issue 4: Slow Build Times
Solution:
- Use Docker layer caching effectively
- Optimize your Dockerfile (copy package files first, then source code)
- Consider using Coolify’s build cache settings
- For very large projects, upgrade to a server with more CPU
Issue 5: Environment Variables Not Working
Solution:
- Ensure you’re not committing
.envfiles to Git - Double-check variable names (they’re case-sensitive)
- Rebuild the application after adding variables
- For Next.js, remember
NEXT_PUBLIC_prefix for client-side variables
Backups and Maintenance
Don’t forget about backups! This is crucial when self-hosting.
Database Backups
Coolify has built-in backup functionality:
- Go to your database resource
- Find “Backups” section
- Configure:
- Backup frequency: Daily, weekly, etc.
- Retention: How many backups to keep
- S3 bucket for remote storage (recommended)
Server Snapshots
In Hetzner Cloud Console:
- Select your server
- Click “Create Snapshot”
- Give it a descriptive name
Schedule regular snapshots (weekly or monthly) as a safety net.
Updating Coolify
Coolify releases updates regularly. To update:
ssh into your server
sudo coolify upgrade
Or use the UI: Settings → Updates → Check for Updates
Performance Tips
Using Cloudflare CDN
Even though you’re self-hosting, you can still leverage Cloudflare’s free CDN:
- Transfer your domain to Cloudflare (or just use their nameservers)
- In Cloudflare dashboard, enable the orange cloud for your DNS records
- Configure caching rules and page rules as needed
This gives you edge caching, DDoS protection, and faster global performance.
Multiple Projects on One Server
The CPX21 server can easily handle 3-5 small to medium projects. To optimize:
- Use resource limits to prevent one app from hogging resources
- Consider using Redis for caching shared data
- Monitor resource usage: Settings → Server → Metrics
When you outgrow one server, you can either:
- Upgrade to a bigger server
- Add additional servers and load balance
Optimizing Docker Images
Keep your Docker images small:
# Use alpine variants
FROM node:18-alpine
# Multi-stage builds
# Only copy what's needed for production
# Clean up after installing dependencies
RUN npm ci && npm cache clean --force
Monitoring and Logs
Built-in Monitoring
Coolify provides basic monitoring:
- CPU usage
- Memory usage
- Disk usage
- Network traffic
Access it at: Settings → Server → Metrics
Application Logs
View real-time logs for any application:
- Click on your application
- Go to “Logs” tab
- Choose:
- Build logs
- Application logs
- Container logs
You can also SSH into your server and use Docker commands:
docker logs -f container_name
External Monitoring
For production applications, consider setting up:
- Uptime monitoring: UptimeRobot (free tier available)
- Error tracking: Sentry
- Application monitoring: New Relic or Datadog
Cost Breakdown
Here’s what you can expect to pay monthly:
Hetzner Server (CPX21):
- €7.93/month (~$8.50)
- Can host multiple small to medium projects
Domain:
- $10-15/year (varies by registrar)
Optional:
- Larger server if needed: €15-50/month
- Additional storage: €0.60/month per 10GB
- Backups to S3: Few dollars/month
Total: About $10-15/month for most setups
Is This Migration Right for You?
You should make the move if:
- You’re comfortable with basic server management
- You have multiple projects to host
- You want more control over your infrastructure
- You enjoy learning and tinkering
- Your projects don’t require edge computing
Conclusion
Moving from Vercel to Hetzner with Coolify was one of the best technical decisions I made this year. Not only did it give me more control and flexibility, but it also deepened my understanding of how modern deployment pipelines work.
The initial setup took me about 3-4 hours (including troubleshooting), but now deployments are just as smooth as they were on Vercel. I’m hosting 6 different projects on a single server, all with automatic deployments, SSL certificates, and proper domain management.
Is it for everyone? I think yes. It helps to be a person that enjoys understanding how things work under the hood and wants more bang for your buck, but I highly recommend everyone giving it a shot.
Feel free to reach out if you run into any issues during your migration. Happy self-hosting!