Porting from Heroku

Andrew Snow
Written by Andrew Snow ยท 5 September 2026

If you've spent years on Heroku, you have a mental model of how hosting works: an app, some dynos, a Procfile, a pile of config vars and a few add-ons. The good news is that Tapitalee was built by people with the same mental model. Almost everything you know carries straight across; the difference is that the resources end up in your own AWS account, and you pay AWS for them directly.

This is a quick, friendly tour of how the Heroku pieces map onto Tapitalee. Each section links to the detailed docs if you want the exact commands and options. If you'd rather just have a checklist, jump to Steps to porting an app.

Also see the Heroku vs AWS cost comparison.

Summary

Heroku ran your app on its infrastructure and sent you a bill. Tapitalee runs your app on AWS infrastructure that you own, and acts as the control plane: it creates the containers, databases, buckets and networking, keeps them wired together, and gives you a CLI and dashboard that feel a lot like the ones you're used to. Nothing sensitive comes to rest on Tapitalee's servers, and if you ever stopped using Tapitalee, your AWS resources would keep running.

Here's the shape of it:

Heroku Tapitalee Underneath
AppAppECS Fargate service in your VPC
DynoProcess / containerFargate task
SlugContainer imagePrivate ECR registry
Config varsVariables & secretsAWS Secrets Manager
Add-onsAdd-onsRDS, ElastiCache, S3, EFS...
TeamTeamOne AWS account

Detailed guide: -> Porting from Heroku

Dynos and the Procfile

Each process type in your Procfile becomes a process in Tapitalee, with its own CPU, memory and container count. The internet-facing one is called default rather than web, because it can serve non-HTTP traffic too. Everything else stays on the private network.

Heroku Tapitalee
web:default process
worker:tapit create process name=worker command="..."
release:Pre-deploy step
heroku ps:scale web=3tapit set process name=default demand_count=3

There's no fixed menu of dyno sizes, and any process can mix in spot containers at roughly a third of the on-demand price.

Release phase, one-off dynos and the Scheduler

These three Heroku features all collapse into one Tapitalee idea: a command. You define a command once (say, rails db:migrate) and then decide how it runs.

Heroku Tapitalee
Release phasePre-deploy steps (a failure aborts the deploy)
heroku run rails consoletapit run 'rails console'
heroku run:detached rake footapit create task 'rake foo'
Heroku SchedulerScheduled commands

One-off tasks run in a fresh container with the app's full environment, exactly like a one-off dyno, and every task's output is kept for later. There's also a browser terminal in the dashboard if you're on your phone and just need to poke at something.

Config vars and secrets

Config vars are just called variables. The one upgrade is that you can flag a variable as a secret, which stores it in AWS Secrets Manager, masks it in listings and hides it from team members who don't have rights to view secrets.

Add-ons still inject their own variables (DATABASE_URL, REDIS_URL and so on) in the same URL formats Heroku used, so most frameworks need no code change. See Variables.

Add-ons

This is where the "your own AWS account" part becomes real. A Heroku add-on was a third-party service you rented through the marketplace. A Tapitalee add-on is an actual AWS service, provisioned in your account, with the IAM permissions and network rules wired up so your app can reach it and nothing else can. You still get a variable in your app; you just also get an RDS instance you can see in your own AWS console.

Heroku add-on Tapitalee add-on
Heroku PostgresRDS (PostgreSQL)
JawsDB / ClearDBRDS (MySQL)
Heroku RedisElastiCache
BucketeerS3 (no access keys needed)
Automated Certificate ManagementSecureProxy (Let's Encrypt) or Cloudflare
Datadog / PapertrailDatadog add-on, or built-in CloudWatch Logs

Add-ons can be shared between apps in the same team, like heroku addons:attach, and can be shared read-only, which is handy for reporting tools or an AI agent that should see production data but never touch it. Automatic daily snapshots are on by default, and the Backup add-on can copy them into an immutable vault that nobody, not even the account root user, can delete.

Some marketplace add-ons don't have an equivalent because they don't need one. Sentry, SendGrid, New Relic and friends are ordinary SaaS products that work from inside a container over HTTPS; sign up directly and store the key as a secret. The notable gap is static outbound IPs (QuotaGuard, Fixie): containers have dynamic addresses, and the docs describe the workaround. The full equivalence list, including what's missing, is in Add-ons.

Heroku Postgres to RDS

Heroku Postgres bundled RAM, storage and connection limits into a plan name. On RDS those are separate knobs: pick an instance class, pick a storage size, and turn on Multi-AZ if you want the high availability that came with Premium plans. Credentials come to your app as a secret DATABASE_URL in the same postgres:// format.

Heroku Postgres plan RDS starting point
Essential / Minidb.t4g.micro
Standard-0db.t4g.medium
Standard-2db.m7g.large
Premium-NSame as Standard-N with multi_az=true

Moving the data is the classic dump-and-restore: Tapitalee's utility image lets you do this easily and the step-by-step data migration are covered in Steps. See Databases for database sizing and compatibility details.

Deploys, domains and review apps

git push heroku main becomes tapit image deploy: it builds the image (with Cloud Native Buildpacks if you have no Dockerfile), pushes it to your ECR registry and rolls it out. New containers must pass health checks before old ones are retired, and a failed release rolls back on its own. Rolling back manually is just redeploying an earlier image tag.

There are no pipelines. Staging and production are simply separate apps: your CI step (a GitHub Action, say) pushes the image to both, and you deploy each one whenever you like.

Heroku Tapitalee
git push heroku maintapit image deploy
heroku rollbacktapit create deploy docker_tag=...
PipelinesSeparate staging and production apps
Review appsPreview apps (auto-delete after N days)
HEROKU_API_KEY in CIScoped deploy tokens
heroku logs --tailtapit show logs -f

Domains work the same way, with one wrinkle: there's no herokuapp.com router with a stable IP. Your app gets a *.dns.tapitalee.net hostname to CNAME to, and the Route53 or Cloudflare add-ons can keep DNS records current for you. TLS certificates come from Let's Encrypt via the SecureProxy add-on, or from Cloudflare if you'd rather front the app with a tunnel and WAF.

Review apps become preview apps: one per pull request, inheriting the parent's config, costing nothing when deleted, and cleaning themselves up. There are prebuilt GitHub Actions for the whole flow. See Preview apps and Domains.

What you gain by moving

  • Your bill is the AWS bill. Per-second compute, spot pricing, scale-to-zero environments, and a flat Tapitalee subscription with no markup on top.
  • Any region. Not just US and EU. Each team gets a VPC per region, and Private Spaces-style isolation is the default rather than an upsell.
  • Real cron, real disks, real VMs. Scheduled commands with cron syntax, EFS volumes and EC2 instances when a container isn't the right shape.
  • Private by default. Only the default process faces the internet; databases only accept connections from the app unless you say otherwise. Tailscale and SSH jump-host add-ons get you in from your laptop.
  • Nobody needs AWS credentials. Developers deploy, tail logs and open consoles through Tapitalee. Removing a team member revokes everything at once.

Where to start

For a typical Rails, Django or Node app with a Postgres database, expect the port to take an afternoon, most of which is AWS provisioning time and copying data. The ordered checklist, with heroku and tapit commands side by side, is at Steps to porting an app.

If you'd like to see how this played out for a real team, read our case study on moving a SaaS product from Heroku to AWS. And if you'd rather have a hand with the migration, get in touch.

Ready to get started?

Get in touch to talk through what fits your team, or jump straight in and connect your AWS account.