Leaving Heroku for Europe: a practical migration guide
Heroku defined what deploying an app should feel like: push code, get a URL, never think about servers. If you are moving to Europe for data residency, cost or both, you should not have to give that feeling up. This guide walks through what actually changes when you move a typical Heroku app (a web process, config vars, Heroku Postgres, a custom domain) to klickops, and a migration path you can do in an afternoon.
Why teams move in the first place
Heroku runs on AWS, and Salesforce is a US company. That means your data sits under US jurisdiction, including the CLOUD Act, which can compel US providers to hand over data regardless of which region it is physically stored in. For many European teams, especially those serving Swiss or EU customers in regulated industries, that is a compliance conversation they would rather not keep having.
klickops takes the opposite position: it is owned and operated by a Swiss company (Natron Tech AG) on hardware it owns in Swiss data centres, certified to ISO 27001 and ISO 9001, with no hyperscaler underneath. Your data answers to Swiss law alone. A Swiss region of a US cloud does not give you that; the operating company is still American.
Dynos versus metered usage
The biggest mental shift is billing. Heroku charges per dyno: you pick a dyno type, and you pay for it around the clock whether it is busy or idle. Sizing up means picking a bigger dyno and paying the new flat rate from that moment on.
klickops has no dyno types. CPU and memory are auto-sized, and you are billed hourly on what your workloads actually consume, drawn from a monthly usage credit that comes with your plan and is worth more than the plan price (Hobby is CHF 9 for CHF 10 of credit, Pro is CHF 79 for CHF 100). If usage goes past the credit, the difference comes out of a prepaid wallet you top up deliberately, never an open-ended card. A free plan covers one small stack: one app, one database, inside a small envelope, with no card required.
In practice this means an app that idles at night costs less than one that is busy all day, and scale-to-zero apps cost nothing at all while they sleep.
Add-ons versus built-in services
On Heroku, almost everything beyond the dyno is an add-on with its own vendor, its own bill and its own data location. On klickops the essentials are part of the platform, run by the same operator, in the same Swiss data centres:
- Managed high-availability PostgreSQL with automated backups, point-in-time restore and a built-in query console.
- S3-compatible object storage with versioning and lifecycle rules.
- Block and shared (NFS) persistent volumes you can resize live.
- Custom domains with automatic TLS certificates.
- Scheduled backups with one-click restore, plus live metrics, logs and alerts.
Step 1: containerize, or just connect the repo
Heroku builds your app with buildpacks. klickops starts at the container image, and you have two ways in. The simplest is to connect your GitHub or GitLab repository: klickops builds and deploys automatically on every push, with per-branch preview environments if you want them.
If you prefer explicit control, add a Dockerfile and push the image to any registry klickops can pull from. A typical Node web process translates directly:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "server.js"]Step 2: move config vars and secrets
Export your Heroku config in one shot, then split it into two piles: plain configuration (log levels, feature flags, public URLs) and secrets (API keys, tokens, credentials).
In klickops, plain values go on the app as environment variables, and secrets are stored separately as managed secrets that the platform injects at runtime. The dashboard never shows a secret value back to you after you save it, which is the behavior you want. Skip DATABASE_URL for now; the new database will provide its own connection string in step 3.
heroku config -s --app your-app > heroku.envStep 3: move the database
Create a managed PostgreSQL instance in your klickops project, then move the data with a standard dump and restore. Heroku's backup tooling already produces a pg_restore-compatible archive:
Copy the connection string from the database page in klickops, restore into it, and point your app's DATABASE_URL at the new instance. For most databases under a few gigabytes the whole transfer is minutes. If you need a near-zero-downtime cutover, do a first restore ahead of time, put Heroku in maintenance mode for the final sync, and re-run the dump and restore for the delta window.
heroku pg:backups:capture --app your-app
heroku pg:backups:download --app your-app
pg_restore --no-owner --no-acl -d "$NEW_DATABASE_URL" latest.dumpStep 4: domain and TLS cutover
Deploy the app on klickops and test it on the platform subdomain it gets automatically, with HTTPS already working. When you are happy, add your custom domain in the dashboard: klickops issues and renews the TLS certificate for you, so there is no certificate step to script.
Before the switch, lower the TTL on your DNS record to something short (300 seconds is typical) and wait for the old TTL to expire. Then repoint the record from Heroku to klickops. Traffic drains over within minutes, both ends serve valid TLS during the overlap, and you can keep the Heroku app running for a day as a fallback before scaling it down.
What you keep, and what you gain
The workflow you liked survives the move: push to deploy, rollbacks, logs, a clean dashboard. What changes underneath is that every app is now a standard Kubernetes Deployment in a namespace, with no custom operator and no proprietary resources. You can export everything as clean YAML at any time and run it on any cluster, so the exit door you are walking through now stays open at the next stop too.
And the compliance answer gets simple: the app, the database, the backups and the object storage all live on Swiss-owned hardware in Swiss data centres, under Swiss law. That is one sentence in your next vendor review instead of ten pages.
Ready to try it on Swiss infrastructure?
Private beta, free plan for one small stack, no card required.