Do Heroku
Dynos become services, add-ons become templates with volumes, config vars become variables, the Procfile becomes a start command. A step-by-step move.
Concept mapping#
| Heroku | Skiffly | Notes |
|---|---|---|
| App | Project with one or more services | one service per process type |
Dyno type (web, worker) | Service | web gets a domain; workers run without one |
| Pipeline stages (staging, production) | Environments | one project, staging and production environments |
| Review apps | PR preview environments | enable PR previews on the project |
| Buildpacks | Railpack | detects the same languages; or add a Dockerfile |
Procfile | Start command per service | web: gunicorn app:app → start command gunicorn app:app |
| Config vars | Variables | ${{Postgres.DATABASE_URL}} references instead of copied URLs |
| Heroku Postgres / Redis add-ons | Postgres / Redis templates with volumes | you own the instance and its data |
| Heroku Scheduler | Cron schedule on a service | 0 * * * * |
heroku run bash | skiffly ssh | shell in the running container |
heroku logs --tail | skiffly logs -f | |
heroku ps:scale web=2 | replicas | Settings → Deploy → Replicas |
| Dyno sizes | CPU / memory limits per service | any value within the plan |
*.herokuapp.com | *.skiffly.cloud | custom domains with automatic TLS |
heroku.yml | .skiffly/skiffly.ts | config as code |
| Monthly invoice | Prepaid balance | PayPal or crypto |
Step by step#
-
Sign in at app.skiffly.dev with GitHub; the code must be on GitHub (Heroku's git remote is not a source).
-
Create the project and a service per process type:
skiffly initin the repository,skiffly upforweb. For aworker, create a second service from the same repository and set its start command to the worker line of theProcfile; leave it without a domain. -
Databases.
skiffly deploy --template postgres(andredis). Copy the data:heroku pg:backups:capture -a my-app && heroku pg:backups:download -a my-app # latest.dump skiffly connect postgres --print pg_restore -d "<url>" --no-owner --clean --if-exists latest.dump skiffly proxy delete <port> -
Config vars.
heroku config -s -a my-app > vars.env, drop the add-on URLs, then:skiffly variables set --skip-deploys $(cat vars.env | xargs) skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}' 'REDIS_URL=${{Redis.REDIS_URL}}' skiffly redeploy -
Port. Heroku sets
$PORT; so does Skiffly. Nothing to change if the app already reads it. -
Release phase. There is no release phase hook. Run migrations from the start command (
sh -c "npm run migrate && npm start") or as a one-off:skiffly ssh -- npm run migrate. -
Scheduler. For each scheduled job create a service from the same repository with a cron schedule and the job's command as the start command.
-
Domain. Add the custom domain, create the CNAME and TXT records, verify, then switch DNS from Heroku once the certificate is issued.
-
Turn Heroku off after a few days of traffic on Skiffly.
Differences worth knowing#
- Skiffly services do not sleep unless you turn on App Sleeping; Free-plan and idle hobby apps are not stopped on a timer.
- The filesystem is ephemeral like a dyno's, but you can attach a persistent volume for uploads or SQLite.
- Logs are kept for the last 5 000 lines per deployment; forward them from the app if you need history.
- Add-on marketplace: pick from 136 templates instead; you run the instance and pay for its resources, not a per-add-on price.