We Moved PAX ERP Off AWS

·5 min read·Matthew Obey
InfrastructureSecurityERP

Earlier this year, I wrote about how PAX ran on AWS. That was our setup until Q3, when we moved production to a local server we own and operate.

PAX has a steady, predictable workload. Once we compared the AWS setup with what the application actually needed, moving it made practical sense.

What Prompted the Move

The old system ran the application on EC2 and PostgreSQL on RDS. The monthly cost was manageable, and the system worked most of the time. PAX never came close to needing the flexible capacity we were paying for.

The bigger frustration was a pattern of brief connectivity gaps. A user could be unable to reach PAX while the application, database, and server all reported normal operation. By the time we investigated, the site was usually working again and AWS showed no downtime.

We never found a clear cause. The problem could have been in the network path, our configuration, or the interaction between several layers. The logs and metrics never explained what the user had experienced.

Our AWS setup used one application server and a single-AZ RDS database. The automated backups and point-in-time recovery worked as described in our earlier post. Keeping a second application server and database ready for automatic failover would have required a larger and more expensive setup.

For PAX, owning and operating a smaller system we could fully understand made more sense.

What Runs PAX Now

PAX now runs on a dedicated Linux server with PostgreSQL, the Node application, Nginx, and standard system services. Cloudflare remains in front of the application. A Cloudflare Tunnel carries web traffic over an outbound connection, so the application and database do not need public inbound ports. Cloudflare has been refreshingly easy to work with compared with some of the networking work we did in AWS.

The tenant architecture stayed the same. Each company has its own PostgreSQL database and runtime configuration. PAX identifies the tenant before making a database query, keeping company data separate.

The stack uses common, well-documented components. One person can follow a request from Cloudflare through the application to PostgreSQL. It fits the traffic and team size PAX has today.

Where the Redundancy Comes From

The production server is one physical machine, so a hardware failure would mean restoring PAX on another machine. We keep everything needed for that recovery in more than one place.

The PostgreSQL databases are backed up to encrypted offsite object storage. Transaction logs are archived continuously, with regular full and differential backups providing multiple recovery points.

Source code, deployment files, private configuration, and local Git history are backed up separately. We also keep an encrypted removable backup with ordinary files and logical database exports. It can be disconnected and stored away from the server and cloud account.

Before cutover, we restored the databases in a separate environment and confirmed that the data and permissions came back correctly. We also restored files from the source and configuration backups.

To our knowledge, power failures at this location have never lasted more than a few minutes. The UPS provides additional runtime during a power failure and can shut the server down cleanly if its battery becomes low. Local monitoring covers PAX, the databases, and the age of the latest backups.

Deployments Became More Boring

The AWS version of PAX used GitHub workflows and deployment webhooks. Deployments now start from our development workstations with one command.

Before anything leaves a workstation, the command runs focused tests, builds the client, packages the release, and creates a SHA-256 manifest. It then transfers the release over SSH.

The production server verifies the files and prepares a separate runtime tree for each tenant. A release is accepted only after Nginx and every tenant health check passes. A few previous releases remain available for rollback, and older build files are removed automatically.

The Cutover

We handled the databases first and changed the routing afterward.

We froze production writes on AWS, took a final RDS snapshot, exported and verified every production database, and copied the archives to the new server. After restoring them, we checked the database structure and permissions, made a fresh offsite backup, started PAX, and tested each production domain before reopening access. The maintenance window lasted 34 minutes and took place during a scheduled low-traffic period.

During the observation period, the former AWS environment stayed out of service and was kept only for rollback. All production writes went to the new server.

What It Costs

We estimate that running PAX on the new server costs approximately 82% less than the former AWS setup. Electricity is now the main ongoing expense.

What We Gained

The biggest improvement is visibility. We know exactly where PAX is running, and we can inspect the hardware, operating system, database, application, firewall, backups, and network path ourselves.

The intermittent connectivity problem on AWS was the opposite experience. Users sometimes could not reach PAX while every dashboard and application log looked normal. We spent time looking at healthy monitors without getting any closer to an answer.

The new system is simple enough for a small team to understand from end to end. We built it, documented it, restored it, and know where to look when it behaves unexpectedly.

Written by

Matthew Obey
October 1, 2026

Questions about PAX?

Send us a message if you want to know more about how PAX runs.

Share this post

Join our newsletter

Subscribe to get the latest blog posts and manufacturing insights delivered to your inbox.