Skip to main content

Migrate from Vercel to Coolify

·OSSAlt Team
coolifyvercelnextjsself-hostingdevops

TL;DR

You can replace Vercel with Coolify on a $6-15/month Hetzner VPS and get 95% of the same developer experience — git push deploy, auto SSL, preview deployments, env vars — while cutting costs by 80-95%. The migration takes 3-4 hours. You give up: Vercel's global CDN edge network, Vercel Analytics, and zero-maintenance infrastructure. If you're spending more than ~$40/month on Vercel, the math strongly favors self-hosting.

Key Takeaways

  • Coolify is the self-hosted Vercel — git push deploy, SSL, domains, preview deployments, one-click databases, all from a web UI
  • Cost savings are dramatic — one reported migration went from $850/month on Vercel to $14/month on Coolify + Hetzner
  • Next.js requires output: 'standalone' in next.config.mjs to build a self-contained Docker image
  • Nixpacks handles most cases automatically — Coolify detects Next.js and builds without a Dockerfile; use Dockerfile for full control
  • ISR and image optimization work, but require more care when scaling horizontally (single instance = no problem)
  • What you give up: Vercel's Edge Network CDN, Vercel Analytics, automatic scaling from 0 to millions, and zero ops overhead

Why Developers Are Leaving Vercel

Vercel is genuinely excellent. The deployment experience is unmatched, and for small projects or teams with engineering bandwidth to burn, it's worth the premium. But the pricing model creates problems at scale:

  • $0.15/GB bandwidth overage beyond the 1 TB Pro plan inclusion
  • $0.60 per million function invocations plus CPU and memory charges
  • $20/seat/month for the Pro plan, per team member
  • A viral Reddit post or Product Hunt launch can generate a $500+ bill overnight

Real-world reports document a client paying $850/month on Vercel (bandwidth + Pro seats + edge functions) who migrated to Coolify + Hetzner for $14/month. Another documented case describes escaping a $95,000 Vercel bill. These are outliers, but the underlying math is structural: Vercel's pricing is designed for enterprise, not indie developers or growing startups.

"Moving from Vercel to Hetzner with Coolify was one of the best technical decisions I've made — not only did it give me more control, but it deepened my understanding of how modern deployment pipelines work." — Developer, Hetzner migration post


What Coolify Gives You

Coolify is an open-source, self-hostable PaaS with 32,000+ GitHub stars. It runs on your own server and provides:

FeatureVercelCoolify
Git push → deploy
Auto SSL (Let's Encrypt)
Preview deployments (PRs)
Custom domains
Environment variables
One-click databases✅ (add-on $$)✅ (free)
Build logs
Rollbacks
Edge CDN✅ ✅ (global)❌ (single region)
Serverless scaling✅ (auto)❌ (manual)
Analytics✅ ($9+/mo)❌ (use PostHog/Plausible)
Vendor lock-inHighNone
Monthly cost (1 app)$20-200+$6-15

Part 1: Server Setup

Choose a VPS

Coolify's minimum requirements: 2 vCPUs, 2 GB RAM, 20 GB storage. Recommended providers:

ProviderInstanceMonthlyNotes
HetznerCX22~€4.35Best value, EU data centers
HetznerCX32~€8.49Comfortable for 2-3 Next.js apps
DigitalOceanBasic 2GB$12Good US/EU coverage
VultrRegular 2GB$12Wide global regions

Recommended: Hetzner CX32 (4 vCPU, 4GB RAM) at €8.49/month — comfortable headroom for a production Next.js app plus a database.

Install Coolify

SSH into your server as root (required) and run the one-line installer:

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

This installs Docker, sets up Coolify's container stack, and starts the web UI at port 8000. The install takes about 2 minutes.

Tip: On Ubuntu 24.04 LTS (recommended), the installer works without any extra configuration. On non-LTS Ubuntu versions, use the manual installation method from Coolify's docs.

After installation, access the Coolify dashboard at http://YOUR_SERVER_IP:8000 and create your admin account.

Point a Domain at Your Server

Add an A record pointing to your server's IP:

*.yourdomain.com  →  YOUR_SERVER_IP
yourdomain.com    →  YOUR_SERVER_IP

The wildcard * record lets Coolify auto-provision subdomains for preview deployments (e.g., pr-42.app.yourdomain.com).


Part 2: Prepare Your Next.js App

Enable Standalone Output

The most important change: add output: 'standalone' to your next.config.mjs. This creates a self-contained .next/standalone folder with everything needed to run the app — no node_modules required at runtime.

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  // Your existing config...
};

export default nextConfig;

This reduces Docker image sizes from 7+ GB to under 400 MB in most cases.

Coolify uses Nixpacks by default — it auto-detects Next.js and builds without a Dockerfile. For most apps this works fine. For production control, use this multi-stage Dockerfile:

# Dockerfile
FROM node:20-alpine AS base

# Stage 1: Dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
  else echo "Lockfile not found." && exit 1; \
  fi

# Stage 2: Builder
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Build with standalone output
ENV NEXT_TELEMETRY_DISABLED 1
RUN \
  if [ -f yarn.lock ]; then yarn run build; \
  elif [ -f package-lock.json ]; then npm run build; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
  else echo "Lockfile not found." && exit 1; \
  fi

# Stage 3: Production runner
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy public assets
COPY --from=builder /app/public ./public

# Copy standalone output
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
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

This is adapted from the official Next.js Docker example. Commit this to your repo root.

Create a .dockerignore

Dockerfile
.dockerignore
node_modules
npm-debug.log
.next
.git
.gitignore
README.md
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

Part 3: Deploy to Coolify

Connect Your GitHub Repository

  1. In Coolify dashboard → SourcesAddGitHub App
  2. Install the Coolify GitHub App on your account/org
  3. Select your Next.js repository

Create a New Application

  1. Go to your ProjectNew ResourceApplication
  2. Select GitHub as the source, choose your repo and branch (main)
  3. Set Build Pack:
    • If you added a Dockerfile: choose Dockerfile
    • Otherwise: choose Nixpacks (auto-detection)
  4. Set Port: 3000
  5. Set your Domain: https://app.yourdomain.com

Coolify automatically provisions an SSL certificate from Let's Encrypt when you use https:// in the domain field.

Add Environment Variables

Go to your application → Environment Variables. Add your secrets one by one, marking sensitive values as Secret (encrypted at rest):

NEXT_PUBLIC_APP_URL=https://app.yourdomain.com
DATABASE_URL=postgresql://user:pass@host:5432/db
NEXTAUTH_URL=https://app.yourdomain.com
NEXTAUTH_SECRET=your-secret-here
STRIPE_SECRET_KEY=sk_live_...
RESEND_API_KEY=re_...

Important: NEXTAUTH_URL must be updated from your Vercel URL to your new domain. Same for any webhooks registered in Stripe, GitHub, etc.

First Deploy

Click Deploy. Coolify will:

  1. Clone your repo
  2. Run the Docker build (or Nixpacks build)
  3. Start the container
  4. Route traffic through Traefik (the built-in reverse proxy)
  5. Issue the Let's Encrypt certificate

Watch the Deployment Logs for the build output. A cold build takes 2-5 minutes.


Part 4: Set Up Auto-Deploy (Git Push → Deploy)

Automatic Webhook Deployment

Coolify creates a webhook in your GitHub repo automatically when you connect via the GitHub App. Every push to your configured branch triggers a rebuild and redeploy with zero downtime.

Preview Deployments for Pull Requests

To enable Vercel-style PR preview URLs:

  1. Application → AdvancedPreview Deployments → Enable
  2. Configure the preview URL pattern: pr-{{pr_id}}.app.yourdomain.com
  3. Add preview-specific environment variables (separate from production)

When you open a PR, Coolify deploys a preview URL and posts a comment with the link — identical to Vercel's behavior.

GitHub Actions Integration (Advanced)

If you want tests to pass before deploying, use the Coolify API:

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Coolify deployment
        run: |
          curl -X GET "${{ secrets.COOLIFY_WEBHOOK_URL }}" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}"

Set COOLIFY_WEBHOOK_URL and COOLIFY_API_TOKEN as GitHub repository secrets (get these from Coolify: Settings → API → Create Token).


Part 5: Database Migration

Vercel's Postgres add-on costs $20+/month. In Coolify, add a Postgres database for free (using your server's storage):

  1. Project → New ResourceDatabasePostgreSQL
  2. Set version, credentials, and database name
  3. Copy the internal connection string: postgresql://user:pass@db:5432/mydb

Internal networking: Services in the same Coolify project can communicate via container name (e.g., db:5432). Use the internal URL for your app's DATABASE_URL to avoid unnecessary network hops.

Run your migrations against the new database before switching DNS:

# Connect from your local machine via SSH tunnel
ssh -L 5432:localhost:5432 root@YOUR_SERVER_IP

# Run migrations
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" npx prisma migrate deploy
# or
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" npx drizzle-kit migrate

Part 6: DNS Cutover

Zero-downtime migration steps:

  1. Keep Vercel running until you've verified Coolify works
  2. Deploy to Coolify, test thoroughly at https://staging.yourdomain.com
  3. Verify all env vars, webhooks, and integrations work
  4. Lower your DNS TTL to 60 seconds (24 hours before switching)
  5. Update DNS: point yourdomain.com A record to your VPS IP
  6. Wait for propagation (~5 minutes with low TTL)
  7. Verify production works on Coolify
  8. Deactivate the Vercel project (keep 30 days before deleting)

What You Give Up

Be honest about the trade-offs before migrating:

1. Global CDN Edge Network

Vercel distributes your app across 40+ edge regions globally. On a single Hetzner VPS in Nuremberg, users in Singapore see higher latency. Mitigation: Add Cloudflare's free CDN in front (proxy your domain through Cloudflare → static assets are cached globally).

2. Automatic Scaling

Vercel scales to zero and back up automatically. Coolify runs a fixed container — if you get 100K concurrent users, your VPS might struggle. Mitigation: Upgrade the VPS, or use Docker Swarm / multi-server Coolify for horizontal scaling.

3. ISR Caching at Scale

Next.js ISR uses the local filesystem by default. On a single instance this works fine. On multiple instances, caches go out of sync. Mitigation: Single instance = no problem. For multi-instance setups, configure a Redis cache handler.

4. Zero Ops

Vercel handles security patches, runtime updates, and infrastructure. Self-hosting means you own the server. Mitigation: Coolify has an auto-update feature; enable it for non-breaking updates. Budget 1-2 hours/month for maintenance.

5. Vercel Analytics

Not available self-hosted. Alternatives: Plausible Analytics (open source, $9/mo managed or free self-hosted), PostHog (open source, generous free tier).


Cost Comparison

ScenarioVercelCoolify + Hetzner
1 small app$20/mo (Pro)$6/mo (CX22)
1 app + Postgres$40/mo$6/mo
3 apps + 2 databases$60-150/mo$9/mo (CX32)
High traffic (100GB bandwidth)$35/mo$9/mo
Team of 3$60/mo (3 seats)$9/mo

Break-even: If you're paying more than ~$30/month on Vercel, self-hosting pays for itself including the 3-4 hours of setup time.


Methodology


Ready to explore Coolify vs other self-hosted PaaS options? See OSSAlt's Coolify alternatives comparison.

Comments