Docs / Migration Guides / Migrate from Heroku to Self-Hosted

Migrate from Heroku to Self-Hosted

By Admin · Mar 15, 2026 · Updated Apr 24, 2026 · 490 views · 3 min read

Why Leave Heroku?

Since Heroku removed its free tier in 2022, costs have increased significantly. A basic Heroku setup (1 dyno + Postgres + Redis) can cost $30-50/month, while a VPS with equivalent resources costs $5-15/month. Self-hosting also gives you full control over your infrastructure.

Heroku to VPS Mapping

Heroku FeatureSelf-Hosted Equivalent
DynosDocker containers or systemd services
Heroku PostgresPostgreSQL installed directly
Heroku RedisRedis installed directly
Heroku CLI deployGit + deploy script or CI/CD
Config Vars.env file or Docker env
Add-onsSelf-hosted equivalents
SSLCertbot / Let us Encrypt

Export from Heroku

# Export database
heroku pg:backups:capture -a my-app
heroku pg:backups:download -a my-app
# Creates latest.dump (PostgreSQL custom format)

# Export config vars
heroku config -a my-app --shell > .env

# Get application code (should be in Git already)
git clone https://github.com/you/my-app.git

Set Up VPS

# Install Docker
curl -fsSL https://get.docker.com | bash

# Create docker-compose.yml
version: "3.8"
services:
  app:
    build: .
    ports: ["3000:3000"]
    env_file: .env
    depends_on: [db, redis]
    restart: always
  db:
    image: postgres:16
    volumes: [pg_data:/var/lib/postgresql/data]
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    restart: always
  redis:
    image: redis:7-alpine
    volumes: [redis_data:/data]
    restart: always
  nginx:
    image: nginx:alpine
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - certs:/etc/letsencrypt
    depends_on: [app]
    restart: always
volumes:
  pg_data:
  redis_data:
  certs:

Restore Database

# Restore Heroku PostgreSQL dump
docker compose up -d db
docker compose exec -T db pg_restore --clean --no-owner \
    -U myapp -d myapp < latest.dump

Adapt Procfile to Docker

# Heroku Procfile:
# web: gunicorn app:app
# worker: celery -A tasks worker

# Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:3000"]

# Worker as separate service in docker-compose:
  worker:
    build: .
    command: celery -A tasks worker
    env_file: .env
    depends_on: [db, redis]
    restart: always

Deploy Script

#!/bin/bash
# /usr/local/bin/deploy.sh
cd /opt/my-app
git pull origin main
docker compose build
docker compose up -d
docker compose exec app python manage.py migrate
echo "Deployed at $(date)"

SSL and Domain

# Install certbot and get certificate
apt install -y certbot
certbot certonly --standalone -d example.com
# Configure Nginx to use the certificate

Best Practices

  • Use Docker Compose for Heroku-like simplicity
  • Set up CI/CD with GitHub Actions for automated deploys
  • Configure automated database backups
  • Set up monitoring (Uptime Kuma, Prometheus)
  • Use a process manager or Docker restart policies for reliability

Was this article helpful?