Skip to content

Porting an app from Heroku, Scalingo, or elsewhere

Already running somewhere else — Heroku, Scalingo, Render, or just a bare Flask/Rails app? Moving to Allocus is mostly a mechanical translation of your platform's config into a Dockerfile + allocus.yaml. The easiest way is to open the repo in Claude and say "deploy this to allocus" — the skill recognizes these setups and does the translation. Here's what it's doing, so you can do or check it yourself.

The translation table

What you have What it becomes on Allocus
Procfile web: <cmd> Your container's start command → CMD in the Dockerfile.
worker: / clock: process types Extra services in a stack (own build:, no public port).
runtime.txt, .python-version, .nvmrc The version to pin your Dockerfile base image to.
A Postgres / Redis addon (DATABASE_URL, REDIS_URL) A db / redis service in a stack; point the env var at it by name.
app.json / scalingo.json env vars Entries under the service's environment:.
Aptfile or apt buildpack apt-get install … in the Dockerfile.
A static site with a build step Build in one stage, serve with an nginx image.
App binds $PORT (a PaaS convention) Pick a fixed port, set it in allocus.yaml, and bind it.

The end state is always a real Dockerfile committed to your repo — the whole point is that the app no longer depends on the other platform's buildpacks.

Worked example: a Heroku Flask app

Say the repo has this, and no Dockerfile:

Procfile      →  web: gunicorn app:app
runtime.txt   →  python-3.12
requirements.txt
app.py        →  a Flask app, plus a Heroku Postgres addon (DATABASE_URL)

Because there's a database, this becomes a small stack.

Dockerfile (from runtime.txt + the Procfile web command):

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn
COPY . .
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "app:app"]  # (1)
  1. Heroku set the port via $PORT; on Allocus you pick it (8080) and bind it explicitly.

allocus.yaml (the app + its database, replacing the Heroku Postgres addon):

name: my-flask-app
services:
  web:
    build: .
    port: 8080
    expose: true
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app   # was the Heroku addon
  db:
    image: postgres:16
    volumes: ["pgdata:/var/lib/postgresql/data"]
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app

Then set the password once — it goes in the encrypted secret store, not in git — and deploy:

allocus secrets set DB_PASSWORD=$(openssl rand -hex 16)
allocus deploy --wait

Your app is live at https://my-flask-app.<you>.allocus.dev, with its own Postgres running alongside it on your box.

Bring your data

This stands up an empty database. Migrating existing data (a pg_dump restore, for instance) is a one-time step outside the deploy — ask Claude for the exact commands for your setup.

What you give up by leaving

Worth knowing before you move something you depend on, because these are real differences rather than a rough edge that'll be smoothed later:

On Heroku/Render On Allocus
Managed Postgres with automatic backups and PITR Postgres is a container on your box. Nothing backs it upthat's your job.
Dynos scale horizontally One box, one machine's worth of capacity. You can size it up, not out.
An uptime SLA None. One operator, best effort.
A per-app bill that grows One flat price, however many apps you deploy.

The last row is the trade you're making, and the first row is what it costs. For side projects, internal tools and things you'd redeploy from git anyway, that's a good deal. For something with a real durability requirement, keep the database managed and put only the app on Allocus.

If it's a single process with no database

Even simpler — no stack needed. A Dockerfile derived from your Procfile/runtime, plus a two-line allocus.yaml:

name: my-app
port: 8080

See the Quickstart for the single-container path.