Migrate from Heroku to Temps
To move a Heroku app to Temps: deploy the same Git repository (Temps auto-detects the runtime, or uses your Dockerfile), copy your config vars, restore your Heroku Postgres backup into a Temps managed PostgreSQL service, replace add-ons with built-in Temps services, then cut DNS over. The model is the same as Heroku's — push code, the platform builds and runs it — but Temps runs only the web process and doesn't run a release phase, so migrations and workers need the changes described below.
Heroku has no free tier, and on 6 February 2026 it moved to a sustaining engineering model focused on stability and security rather than new features. For a plan-by-plan comparison, see Temps vs Heroku and the best Heroku alternatives.
Procfile → Temps
Heroku uses a Procfile to define process types:
web: node server.js
worker: node worker.js
release: node migrate.js
On Temps, only the web: line is used, as the start command. worker: and release: lines are ignored. You don't need a Dockerfile for a standard app — Temps auto-detects the runtime (see Buildpacks). Add one if you use a custom buildpack or need full control of the image:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Temps picks the container port from the project's port setting, then the image's EXPOSE, then 3000 (see Ports). Bind your app to process.env.PORT with a fallback:
const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0");
Workers
Temps has no worker process type, and a project that doesn't answer HTTP fails its health check. To run a worker next to your web process, deploy the app as a Docker Compose project with a web service and a worker service; services that don't publish a port stay off the proxy. Alternatively, start the worker inside the web container with a process manager.
Release phase and migrations
Temps has no release phase or pre-deploy command, and it does not run database migrations for you. Run them as a one-off step after the deploy — from your CI pipeline or over SSH with docker exec — as shown in Laravel migrations. Don't chain them into the start command: with rolling deploys, restarts or several replicas, they can run more than once at the same time. If your app must migrate on boot, gate it on TEMPS_REPLICA being 1 so only one replica runs it (see reserved variables).
Buildpacks
Heroku buildpacks auto-detect and configure your runtime. Temps does the same from the files in your repository: Next.js, Vite, Python, Go, Rust, Java and more are detected automatically; a plain Node.js server (Express, NestJS) uses the Node preset you pick, while Rails apps use the nixpacks-ruby preset and Laravel the nixpacks-php preset (see supported platforms).
If your app uses a custom buildpack, add a Dockerfile instead. Temps treats any repo with a Dockerfile as a Docker project.
Environment Variables
Export from Heroku CLI:
heroku config -s --app your-app-name > .env
Then add them in Temps → Project → Settings → Environment Variables, or import the file with bunx @temps-sdk/cli environments vars import .env -p my-app -e production.
Remove Heroku-injected variables before importing — they won't apply on Temps:
| Heroku variable | Action |
|---|---|
DATABASE_URL | Map to the POSTGRES_URL Temps injects when you link a managed database (see below) |
REDIS_URL | Remove — linking a Temps managed Redis injects its own REDIS_URL |
HEROKU_APP_NAME | Remove or replace with your own |
HEROKU_RELEASE_VERSION | Remove |
WEB_CONCURRENCY | Keep or set it yourself if your server reads it; Temps doesn't set it |
PORT | Remove — Temps sets it to the project's port |
Heroku Postgres Migration
Export data
# Create a backup on Heroku
heroku pg:backups:capture --app your-app-name
# Download the backup
heroku pg:backups:download --app your-app-name
This creates a latest.dump file in the current directory.
Create and link the Temps database
Temps doesn't create a database for you on deploy: create a PostgreSQL service, then link it to the project. Linking creates a database named <project>_<environment> and injects POSTGRES_URL, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD into that environment.
bunx @temps-sdk/cli services create -t postgres -n my-app-db -y
bunx @temps-sdk/cli services list # note the service ID
bunx @temps-sdk/cli services link --id <service-id> -p my-app
bunx @temps-sdk/cli services env --id <service-id> -p my-app # POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
Restore to your new database
The managed database isn't reachable from the internet: on the Temps server it listens on 127.0.0.1, on a host port assigned from 5432 (docker ps on the server shows it). Copy latest.dump to the server and restore it into the linked database, using the user, password and database name from services env:
scp latest.dump user@your-server:/tmp/
ssh user@your-server
pg_restore --verbose --no-acl --no-owner \
-d "postgresql://<POSTGRES_USER>:<POSTGRES_PASSWORD>@127.0.0.1:<host-port>/<POSTGRES_DB>" \
/tmp/latest.dump
Or open an SSH tunnel and restore from your machine (see Networking).
Test the restore against a scratch database first. Heroku's pg_dump uses the
custom format (-Fc) which pg_restore handles, but verify row counts match
before pointing your app at the new database.
Point the app at it
Your code probably reads DATABASE_URL. Either change it to read POSTGRES_URL, or set DATABASE_URL to the same value as POSTGRES_URL in the environment's variables (copy it from bunx @temps-sdk/cli services env --id <service-id> -p my-app). Then run your migrations once, as described in Release phase and migrations.
Add-on Alternatives
Heroku add-ons that are commonly used and their Temps-compatible replacements:
| Heroku Add-on | Alternative |
|---|---|
| Heroku Postgres | Temps managed PostgreSQL, Neon, or Supabase |
| Heroku Redis | Temps managed Redis |
| SendGrid | Temps transactional email (SES, Scaleway or an SMTP relay), or Resend or Postmark |
| Papertrail | Temps runtime logs |
| New Relic / Librato | Temps monitoring, uptime checks and OpenTelemetry traces |
| Rollbar / Bugsnag | Temps error tracking (Sentry-compatible DSN) |
| Scheduler | Temps cron jobs: .temps.yaml entries that call an HTTP path on a schedule, so a scheduled command becomes an endpoint |
| Cloudinary | Cloudflare Images or self-hosted Imgproxy |
Deploy
# Install Temps if not already done
curl -fsSL https://temps.sh/deploy.sh -o deploy.sh
less deploy.sh
bash deploy.sh
- Go to Projects → New Project in the Temps console
- Connect your Git repository
- Add environment variables
- If using a Dockerfile, Temps detects it automatically; otherwise set your build and start commands
- Click Deploy
Custom Domains
Heroku routes traffic through yourapp.herokuapp.com. On Temps:
- Go to Project → Settings → Domains → Add Domain (or run
bunx @temps-sdk/cli custom-domains create --project-id <id> -d app.example.com -y) - Add the DNS records shown (A or CNAME)
- Temps provisions TLS automatically via Let's Encrypt
Remove the domain from your Heroku app once you've verified the Temps deployment is healthy.
Verify and Cut Over
- Test your app at the Temps preview URL end-to-end (auth, database reads/writes, background jobs)
- Cut DNS over to Temps
- Keep your Heroku app in maintenance mode (not deleted) for 48 hours as a fallback