# AWS to Self-Hosted Migration Cost Starts With Paperwork

- Author: Abdullah Chaudary
- Category: Infrastructure
- Published: 2026-10-02T19:37:01.750Z
- Updated: 2026-10-02T19:37:01.779Z
- Reading time: 7 min
- Tags: AWS, Cloud Migration, Self-Hosted Infrastructure, Nginx, Prometheus, Grafana

> An AWS to self-hosted migration for sensitive data moves stateless services first and databases last, after procurement and KYC. The real cost is the operations you take back in house.

**TL;DR:** For sensitive data, the AWS to self-hosted migration cost is an operations question before it is a hosting question. The clients I move off AWS work in health, education, medical care and research, and they want that data on hardware they own, on-premises or in a colocation rack. The playbook: clear procurement and KYC first, write the infrastructure as code, move stateless services before stateful ones and the databases last, and budget for the backups, patching, monitoring and on-call that AWS used to carry.

## Why Do Health and Research Teams Leave AWS for Self-Hosted Infrastructure?

Health, education, medical and research organisations leave AWS for self-hosted infrastructure because their most sensitive data then sits on servers they own, not on Amazon's. Encryption and access policies on AWS are real controls, but the hardware still belongs to someone else, and for this kind of data that is the part the client will not accept.

That is the common thread across the companies I have done this for: the main reason is how sensitive the data is. On one EdTech platform, [where I owned every server, the infrastructure and the security single-handed](/about), the move to a self-hosted managed cloud cut infrastructure cost by about 70%, with zero downtime. The saving is the headline number; control of the data is the reason.

Everything moves. In these migrations nothing stays behind on AWS. The targets are on-premises infrastructure the client controls, and sometimes dedicated hardware in a colocation facility.

## What Has to Happen Before the First Line of Terraform?

Before the first line of Terraform or Ansible, an on-premises migration for a regulated client goes through procurement, a KYC process and an NDA, and there is a lot of paperwork before anyone writes Terraform or Ansible. Nobody stands up a server for a health or research organisation until both sides know exactly who is holding what.

The work you can plan in a terminal starts after it. Hardware has to be ordered or a colocation contract signed, identities have to be verified on both sides, and every document about the system is covered by the NDA before it is written.

The useful move is to treat that phase as design time:

1. **Inventory everything on AWS.** Every instance, function, bucket, database, DNS record and scheduled job, with what depends on it.
2. **Write the target as code.** Terraform for the infrastructure, Ansible for the configuration of every host, so the new environment can be rebuilt from the repository and reviewed before it exists.
3. **Decide the observability stack before the first service moves.** The client is about to lose the dashboards that came with AWS; the replacement has to be watching from the first cutover.

By the time the paperwork clears, the environment already exists as reviewed code.

## In What Order Should You Move Services Off AWS?

Move services off AWS in order of how hard they are to roll back: the stateless application instances first, then edge functions, then object storage, and the databases last. The order I use is app instance, edge functions, S3 stores, databases.

Each step only depends on what has already moved or still works across the gap:

1. **Application instances.** Stateless, so the new copies can run beside the AWS ones, reading the same data, and take traffic gradually. Rolling back means pointing traffic back.
2. **Edge functions.** Logic that ran at the edge on AWS gets rebuilt on the self-hosted stack, at the reverse proxy or inside the application. It is small, testable code, and it moves once the app it fronts has moved.
3. **Object storage.** Files are copied, then kept in sync until cutover. Copies are cheap to verify and cheap to redo.
4. **Databases.** Last, because they are the one component where a mistake loses data instead of an afternoon.

Moving the databases last also means every other service has already been proven on the new hardware when the riskiest change happens. On the EdTech platform, nginx ran as the reverse proxy in front of the apps and the WordPress site, the same pattern as [this site's self-hosted Next.js and Payload stack behind nginx](/blog/one-vps-nextjs-payload-cloudflare-nginx).

## How Do You Keep a Migration Off AWS at Zero Downtime?

Zero downtime comes from never switching anything that has not already been running in parallel: lower DNS TTLs days ahead, sync storage continuously, replicate the databases live, and only then move traffic. A migration that copies data during a maintenance window is a migration with downtime.

**DNS.** Lower the TTL on every record that will change well before the cutover. Cloudflare's documentation puts it this way: "a longer TTL also means that updates to your records take longer to go into effect." Short TTLs make the switch, and a rollback, take minutes instead of hours.

**Object storage.** Copy buckets to the self-hosted S3-compatible store ahead of time, then re-run the sync until the delta is near zero. rclone's `sync` command does this: "Sync the source to the destination, changing the destination only." It deletes files on the destination to match, and its own documentation warns: "Since this can cause data loss, test first with the `--dry-run` or the `--interactive`/`i` flag."

**Databases.** Replicate, never dump and restore under pressure.

- **MongoDB:** add the new server as a member of the existing replica set and let it sync. MongoDB's documentation recommends adding a new member "initially with `priority :0` and `votes :0`", checking with `rs.status()` that it has reached `SECONDARY`, and only then using `rs.reconfig()` to give it a vote. When the new members are in sync, step the old primary down and remove the AWS members.
- **PostgreSQL:** logical replication streams changes from a publisher to a subscriber, and the PostgreSQL documentation lists replicating between different major versions and between instances on different platforms among its typical uses, so the new server catches up live before cutover.

**Verification.** Before traffic moves, compare record counts and checksums per collection or table, compare object counts and sizes per bucket, and run the application's own smoke tests against the new side. After traffic moves, keep the AWS side read-only until the numbers have matched for long enough to trust them.

## What Is the Hardest Part of an AWS to Self-Hosted Migration?

The hardest parts of an AWS to self-hosted migration are the databases and security, because those are the two things AWS was doing that nobody listed. A managed database came with replication, failover and backups; an AWS account came with identity, network isolation and encryption defaults. On your own hardware, each of those becomes a decision.

For the databases, the move itself is the replication shown above. The lasting work is everything around it: replica sets or standbys on separate hardware, backups that are restored on a schedule to prove they work, and alerts on replication lag.

For security, the whole network is now yours. The baseline: UFW firewalls on every host, private networks between services, TLS everywhere, HashiCorp Vault for secrets, access through VPN, and fail2ban on anything that faces the internet. On sensitive data, the security setup is not a phase after the migration. It is the condition for starting it.

## What Is the Real AWS to Self-Hosted Migration Cost After the Move?

After the move, the real AWS to self-hosted migration cost is lower hosting and higher operations: backups, patching, monitoring and on-call all come back in house. There are always trade-offs, and those four are the ones that never go away.

The monitoring stack I run for these clients is fully self-hosted too:

- **Sentry, self-hosted,** for application errors. Sentry's own documentation sets the floor at 4 CPU cores and 16 GB of RAM plus 16 GB of swap, with 32 GB of RAM recommended, so it is a real workload to size for.
- **Prometheus,** with exporters for every service and for the hardware components themselves, so a failing disk or a hot CPU raises an alert like a failing service does.
- **Grafana,** for the dashboards and the alerting rules on top of Prometheus.

Hardware monitoring is easy to leave out of the plan. On AWS a dying disk is Amazon's problem. On-premises it is yours, and the exporter that reads it is the difference between a planned replacement and an outage.

## Limitations

- Client names, data volumes and hardware specifications are under NDA, so this playbook is generalised from several migrations rather than told as one.
- The roughly 70% cost reduction is from the EdTech engagement; the other migrations were driven by data control.
- The zero-downtime techniques above are the documented methods for each component. The exact cutover runbook depends on the database engine, the data volume and how much write traffic the system takes.
- Self-hosting moves risk rather than removing it. A team without someone owning backups, patching and on-call should not take this on.

On sensitive data, the paperwork is part of the infrastructure. If you have moved a regulated workload off a hyperscaler, what took longer than you planned: the paperwork or the databases?

## References

- [Cloudflare DNS documentation: time to live (TTL)](https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/): how TTL controls how long changes take to reach users.
- [rclone sync](https://rclone.org/commands/rclone_sync/): one-way sync of object storage and its data-loss warning.
- [MongoDB: Add Members to a Self-Managed Replica Set](https://www.mongodb.com/docs/manual/tutorial/expand-replica-set/): adding a member with priority and votes at 0 until it reaches `SECONDARY`.
- [PostgreSQL: Logical Replication](https://www.postgresql.org/docs/current/logical-replication.html): publish and subscribe replication between servers and versions.
- [Sentry self-hosted documentation](https://develop.sentry.dev/self-hosted/): minimum requirements for running Sentry on your own hardware.
- The author's own migration work, described anonymously by industry on the [about page](/about). For a smaller self-hosted setup in the same spirit, see [how this site runs Next.js and Payload on one VPS](/blog/one-vps-nextjs-payload-cloudflare-nginx).
