Skip to main content

Migrating from Heroku

Vessl and Heroku share a lot of concepts (Git-based deploys, environment variables, add-ons, background workers), but the underlying model is different: Vessl runs your app on a server you own, while Heroku runs it on shared infrastructure you rent per dyno.

This guide walks through the main differences and what to do for each.

Concept mapping

HerokuVessl equivalent
DynoApp replica (container)
Procfile web:Start command in app Settings
Procfile worker:Daemon in the Daemons tab
Config varsVariables tab
Heroku PostgresPostgreSQL add-on
Heroku RedisRedis add-on
Heroku SchedulerServer cron job or daemon with a cron loop
Review appsNot available — use staging projects
PipelinesNot available
heroku runConsole tab → Run Command
heroku logs --tailLogs tab
heroku releasesDeployments tab
heroku rollbackDeployments tab → Rollback

Step 1 — Add a server

Vessl is BYOS (bring your own server). You need a VPS running Ubuntu — any provider works (DigitalOcean, Hetzner, Vultr, Contabo, etc.).

A server comparable to a Heroku Standard-1X dyno (512MB RAM) would be a 1GB RAM VPS. Most providers charge $4–6/month for this tier, and you can run multiple apps on it.

Once you have a VPS, follow Add your server to provision it.

Step 2 — Connect your GitHub repository

Vessl deploys from GitHub. If your Heroku app deploys from a Git remote, push it to GitHub first (or use the GitHub integration you may already have).

Follow Connect GitHub to link your GitHub account.

Step 3 — Migrate environment variables

In your Heroku app, run:

heroku config -a your-app-name

This lists all config vars. Copy them and paste them into Vessl's Variables tab. Don't include DATABASE_URL or REDIS_URL — Vessl injects those from add-ons automatically.

Step 4 — Migrate the database

Export from Heroku:

heroku pg:backups:capture -a your-app-name
heroku pg:backups:download -a your-app-name
# Downloads latest.dump

Restore to Vessl:

  1. Add a PostgreSQL add-on to your Vessl app (Overview → Connected Services → Add).
  2. Deploy the app at least once so the add-on is provisioned.
  3. Set up an SSH tunnel to your Vessl server's database:
    ssh -L 15432:127.0.0.1:5432 <ssh-user>@<server-ip> -N
  4. Restore the dump:
    pg_restore --host 127.0.0.1 --port 15432 \
    --username <DB_USERNAME> --dbname <DB_DATABASE> \
    --no-owner --no-privileges latest.dump

Replace <DB_USERNAME> and <DB_DATABASE> with the values from your app's Variables tab.

Step 5 — Handle the Procfile

Heroku reads Procfile to determine how to run your app. Vessl doesn't use Procfile directly — you set these in the app settings instead.

web: process → set as the Start command in Settings → Runtime & Release.

For example, if your Procfile has:

web: gunicorn myapp.wsgi:application --bind 0.0.0.0:$PORT

Set the start command to:

gunicorn myapp.wsgi:application --bind 0.0.0.0:${PORT:-8000}

worker: process → add a Daemon in the Daemons tab with the same command.

release: process → this maps to the Post-deploy command in Settings → Runtime & Release. Common examples:

# Rails
bundle exec rails db:migrate

# Django
python manage.py migrate

# Laravel
php artisan migrate --force

Step 6 — Heroku Scheduler

If you use Heroku Scheduler for recurring tasks, migrate them to either:

  • Server cron jobs (Servers → your server → Cron Jobs) — for host-level tasks
  • Daemon with a cron loop — for tasks that need your app's environment:
    /bin/sh -c "while true; do php artisan schedule:run; sleep 60; done"

Step 7 — Deploy

Create the app in Vessl, select the repository and branch, review the auto-detected settings, and click Deploy.

After the first deploy succeeds, check the Logs tab to confirm your app is running correctly.

Common differences to watch out for

Ephemeral filesystem Like Heroku, your app's container filesystem is ephemeral — files written to disk (outside of a persistent volume) are lost on redeploy. If your app writes user uploads or generated files to disk, add a persistent volume and mount it at the path your app writes to.

PORT binding Heroku injects $PORT and your app must listen on it. Vessl does the same. If your app already handles $PORT correctly for Heroku, it'll work on Vessl without changes.

SSL Heroku provisions SSL automatically on custom domains via their ACM. Vessl does the same via Let's Encrypt. See Custom domains & SSL.

Buildpacks Vessl uses Nixpacks instead of Heroku buildpacks, but the auto-detection logic is similar for common stacks (Node.js, Python, Ruby, Go, PHP). If you have a Dockerfile, Vessl will use that instead.

One-off commands (heroku run) Use the Console tab in Vessl. You can run any command inside the running container — rails console, python manage.py shell, php artisan tinker, etc.