Migrating the Pagecord Rails app from AWS to Hetzner
Today I migrated Pagecord from AWS to Hetzner because my $1000 free AWS credits are coming to an end. For some reason AWS want $70/mo for a t2.medium instance (2 vCPUs and 4GB RAM) with additional block storage, which is simply outrageous in 2025. I can have a Hetzner CAX31 for €14/month which gives me 8 vCPUs, 16GB RAM and 160GB storage. So that's what I bought. No brainer.
Note: I use Hatchbox for server/Postgres/Redis management and deployment (it's a wonderful thing).
There were a few gotchas I encountered when doing this switch, so I thought I'd write them up here.
- The original Hetzner IP address my server was assigned was blocked by Sentry. I learned you can head over to the Primary IPs section in Hetzner and generate a new one, then assign it to your server. First though, you have to shut down the server, unassign the old IP, assign the new IP then restart the server.
- Cloudflare caching caused havoc with Rails assets, Trix in particular (although this will have been a coincidence). In a slight panic I headed to Caching > Configuration and clicked Purge Everything. This sorted it out.
- Some custom domains stopped working 😱 Fortunately they were mine, not those of paying customers. I need to dig into this more but it seems to be to do with Cloudflare SSL settings (on the customer side). For now, I set it to "Flexible" (which probably isn't right, but it has got it working again).
- I am using Cloudfront for Rails assets so I ran an invalidation on Cloudfront to refresh this cache. I'm not sure this was 100% necessary but belt and braces and all that.
To do the actually switch, here's the process I followed:
- [Prep] Add existing custom domains to new app in Hatchbox (they have an API to do this)
- [Prep] Copy over ENV settings to new app in Hatchbox
- [Prep] Test a database restore and make sure the new app works (on temporary domain)
- Turn on maintenance mode on old server
- Turn off background jobs on old server
- Export database from old server
- Import database to new server (using `pg_restore`)
- Restart app for good measure
- Change DNS to point to new server
- Purge Cloudflare cache
- Run Cloudfront invalidation
A few minutes of downtime to export the DB and re-import, but I'll take that.