At a glance
Environment variables
How Heroku maps to Sevalla
Before you begin
- The Heroku CLI, signed in to the account that owns the app.
- The app’s code in a repository on GitHub, GitLab or Bitbucket. Sevalla doesn’t deploy from Heroku’s Git remote. See Git providers.
- A Sevalla company with a payment method.
- The PostgreSQL client tools, at the same major version as Heroku Postgres or newer, if the app uses Heroku Postgres. See Import and export PostgreSQL.
- A maintenance window: the Heroku app is in maintenance mode while you copy the data.
Take stock of the Heroku app
Collect what you’ll recreate, with your app’s name in place ofstorefront:
apps:infoshows the region and the stack. Heroku’susregion runs in northern Virginia andeuin Ireland; the closest Sevalla locations are Ashburn, USA and London, UK. See Locations.addonslists what becomes a database, a bucket or a third-party service. Services that connect through config vars, such as email or error tracking, keep working when you copy their variables.pg:infoshows the PostgreSQL version (PG Version). Create the Sevalla database with the same version or a newer one.heroku.envholds every config var asKEY=value. It contains secrets: don’t commit it.- Keep your
Procfileat hand: each line becomes a process.
Create the databases
Create the PostgreSQL database
orders-db as the Display name, choose the Data center the application will use and a Size with room for the data, then click Create database.Create the Redis database
storefront-redis as the Display name, and choose the same Data center. See Create a database.Create the application
Create it from the repository
Keep Heroku's buildpacks, if you want
Procfile’s web entry. To build the way Heroku does, click the Settings tab, then click Update build strategy under Build strategy. Choose Buildpacks, set the Pack builder to heroku/builder:24, and click Update build strategy. See Buildpacks.Connect the databases
Connect PostgreSQL
orders-db as the Service and turn on Add environment variables to the application. Change the key DB_URL to DATABASE_URL, the name your code already reads, and click Add internal connection.Connect Redis
storefront-redis the same way. Its URL is already called REDIS_URL.postgres://, like Heroku’s. The Redis URL starts with redis://, where Heroku Key-Value Store’s starts with rediss:// (TLS): if your code passes TLS options for it, such as ssl_cert_reqs=None with redis-py, remove them. See Connect an application.
Add the config vars
Clean up the exported file
heroku.env, delete DATABASE_URL, REDIS_URL and the other variables that Heroku add-ons set, such as HEROKU_POSTGRESQL_* or REDIS_TLS_URL: the internal connections replace them. Update any value that contains the Heroku domain, such as allowed hosts, to the application’s sevalla.app domain under its name in the page header.Import the file
heroku.env, or paste its contents into a key field. Keep Available during runtime and Available during buildtime selected, and click Add environment variables.

PORT: Sevalla sets it to the web process’s port. See Environment variables.
Recreate the Procfile processes
release entry, so the job is what runs it, once per deployment, before the new version starts. You can delete the release line from the Procfile.
Commands that use $PORT keep working: Sevalla sets PORT like Heroku does. See Processes.
Copy the PostgreSQL data
Stop writes on Heroku
Download a backup
latest.dump, a custom-format archive.Open the Sevalla database to your computer
orders-db. Click the Networking tab, click Enable under External connection, then click Enable in the dialog. Copy the External hostname, External port and password. See Enable the external connection.Restore the backup
latest.dump with pg_restore, as in Import a database.sevalla.app domain. For the real cutover, empty the database first, for example with pg_restore’s --clean --if-exists options, so the restore doesn’t collide with the rehearsal’s tables.
Copy the Redis data
Skip this when Redis holds only caches or jobs you can lose: the application refills it. Heroku Key-Value Store doesn’t document an export, so copy the keys withDUMP and RESTORE:
Open the Redis database to your computer
Copy the keys
0, the one in both URLs, and keeps each key’s expiry. If RESTORE fails with DUMP payload version or checksum are wrong, the Sevalla database runs an older version than Heroku’s: create it with a newer version. See Import and export Redis.
Deploy and verify
Deploy
Check the deployment
Migrate has been successful.With the API, Get deployment (getDeploymentV3) returns the status, success for Deployed, and Get application deployment logs (getApplicationDeploymentLogsV3) finds the job’s result. APP_ID and DEPLOYMENT_ID come from Deploy with the API, where the job is named release, after its Procfile entry:Test the application
getApplicationRuntimeLogsV3) with search finds a line that a worker logs, for example celery@... ready. for Celery:sevalla apps logs runtime "$APP_ID" --search ready. MCP: search_runtime_logs.Turn on auto-deploy
Move the custom domains
Add each domain
shop.example.com, and click Add custom domain.Verify it
Point it at Sevalla
CNAME to a herokudns.com target) with the pointing record that Sevalla shows. The status changes to Active when Sevalla sees it. See Point the domain.ALLOWED_HOSTS, then deploy.
Shut down the Heroku app
When the domains are Active and traffic reaches Sevalla:- Disable the external connections you opened: on each database’s Networking tab, click Disable under External connection, then click Disable in the dialog.
- Keep
latest.dumpuntil you no longer need it. - Stop the Heroku app with
heroku ps:scale web=0 --app storefront, and delete it withheroku apps:destroy --app storefrontonce you’re sure. Deleting a Heroku app deletes its add-ons and their data.
Deploy with the API
This sequence does what Create the databases to Deploy and verify do in the dashboard, with the Sevalla API and Ingot: it creates the databases and the application, adds the config vars, turns eachProcfile entry into its process, and deploys. A script or an AI agent runs it as is, in one shell, from the root of the repository, where the Procfile is. Each step names the CLI command and the Sevalla MCP server tool that do the same. You need:
- An API key with the Company developer role, in
SEVALLA_API_KEY. - The repository on GitHub, and a GitHub account with access to it connected to Sevalla. The API has no call that reports the connection: without access, the step that creates the application fails with
400andCannot access repository https://github.com/acme/storefront: Repository not found or you don't have access., followed by where to connect a Git account (My account in the dashboard) and, for GitHub, to give Sevalla’s GitHub App access to the repository. - The Heroku CLI, signed in, plus
curl,jqandopenssl.
config.json:
Choose the location
listClustersV3) returns every location with its region ID in location. Save the one closest to the Heroku region, here eu-frankfurt-1 (Frankfurt, Germany) for Heroku’s us region:sevalla resources clusters. MCP: get_platform_options.Look up the process size
listProcessResourceTypesV3). Save S1 for the processes this sequence creates:sevalla resources process-types. MCP: get_platform_options.Look up the database size
listDatabaseResourceTypesV3). Pick a size with room for the data; here DB1, the smallest:sevalla resources db-types. MCP: get_platform_options.Look up the database versions
listDatabaseVersionsV3) returns the versions the location offers, oldest first. Save the newest PostgreSQL and Redis versions, which are at least as new as Heroku’s:sevalla resources db-versions --cluster "$CLUSTER_ID". MCP: not needed, create_database takes the newest version by default.Create the PostgreSQL database
createDatabaseV3). You choose the password, and the API never returns it, so keep it for the connection URL:sevalla databases create --name orders-db --type postgresql --db-version "$PG_VERSION" --cluster "$CLUSTER_ID" --resource-type "$DB_SIZE_ID" --db-name storefront --db-user storefront --db-password "$PG_PASSWORD". MCP: create_database; with connect_to_application_id and env_var_name: "DATABASE_URL", once the application exists, it also does the internal connection and adds DATABASE_URL as a postgresql:// URL.Create the Redis database
createDatabaseV3) again. Skip this step and the other Redis steps if the app doesn’t use Heroku Key-Value Store:sevalla databases create --name storefront-redis --type redis --db-version "$REDIS_VERSION" --cluster "$CLUSTER_ID" --resource-type "$DB_SIZE_ID" --db-name storefront --db-password "$REDIS_PASSWORD". MCP: create_database with type: "redis" and env_var_name: "REDIS_URL".Wait for the databases
getDatabaseV3) shows status ready when a database is up, with its internal hostname and port:sevalla databases get "$PG_ID". MCP: get_database.Create the application
createApplicationV3) creates the application with a web process at S1, which can have custom domains, and doesn’t deploy it:sevalla apps create --name storefront --cluster "$CLUSTER_ID" --source privateGit --git-type github --repository "$REPO" --branch main. MCP: create_application with deploy: false.Connect the databases
createApplicationInternalConnectionV3), once per database:sevalla apps internal-connections create "$APP_ID" --target-id "$PG_ID" --target-type database. MCP: sevalla_api_create with createApplicationInternalConnectionV3 and the same body.Add the environment variables
createEnvironmentVariableV3), once per variable: DATABASE_URL and REDIS_URL in the format the internal connections use, then every entry of config.json:sevalla apps env-vars create "$APP_ID" --key <key> --value <value>, once per variable. MCP: set_environment_variables with all the variables in one call.Create the Procfile processes
createProcessV3), once per Procfile entry other than web: a job with the start policy beforeDeployment for release, and a background worker for every other entry, its command after exec. Each process is named after its entry:Procfile in Recreate the Procfile processes, it prints {"display_name":"worker","type":"worker","entrypoint":"exec celery -A storefront worker"} and {"display_name":"release","type":"job","entrypoint":"python manage.py migrate"}. CLI: sevalla apps processes create "$APP_ID" --name worker --type worker --resource-type "$S1_ID" --command "exec celery -A storefront worker", and for the job --type job --job-start-policy beforeDeployment. MCP: sevalla_api_create with createProcessV3 and the same body.Create the cron jobs
createProcessV3) with type cron, once per Heroku Scheduler job, with its command and a cron schedule in UTC:sevalla apps processes create "$APP_ID" --name "Clear sessions" --type cron --resource-type "$S1_ID" --command "python manage.py clearsessions" --schedule "*/10 * * * *". MCP: sevalla_api_create with createProcessV3 and the same body.Deploy
triggerDeploymentV3) deploys the latest commit of the branch:sevalla apps deployments trigger "$APP_ID". MCP: trigger_deployment.Wait for the deployment
getDeploymentV3) reports the status, which ends as success, failed, cancelled or skipped. The loop keeps polling through a failed request, for up to 30 minutes:sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment.Turn on auto-deploy
updateApplicationV3):true. CLI: sevalla apps update "$APP_ID" --data '{"auto_deploy": true}'. MCP: sevalla_api_update with updateApplicationV3 and the same body.{"build_type": "pack", "pack_builder": "heroku/builder:24"} before you deploy (CLI: sevalla apps update "$APP_ID" --data '{"build_type": "pack", "pack_builder": "heroku/builder:24"}'). The data copy needs the database’s external connection: Toggle external connection (toggleDatabaseExternalConnectionV3) turns it on and off, and Get database then returns external_hostname and external_port. Add application domain (addApplicationDomainV3) adds each custom domain and returns the DNS records in dns_records.