I'm considering moving an existing production backend from managed Supabase to a self-hosted deployment on AWS. The Next.js frontend will stay on a CDN, so the EC2 instance would run PostgreSQL 17, authentication with Google OAuth, PostgREST and RPCs, Storage API, edge and background functions, scheduled jobs, remote job ingestion, AI summary processing, alerts, and apply-link validation.
The workload is mostly read-heavy, with public traffic heavily cached. Ingestion is deliberately limited to one source at a time with concurrency of one, rather than running many ingestion jobs in parallel.
To keep costs low while the product is still early, I'm considering starting with a Graviton t4g.large running ARM64, with 2 vCPUs and 8 GB of RAM. PostgreSQL would use roughly 60–80 GB of gp3 EBS, while uploaded and static files would go to object storage. The alternatives are a t4g.xlarge with 4 vCPUs and 16 GB RAM, or an m-series Graviton instance if sustained database and worker activity makes a burstable instance unsuitable.
For anyone running PostgreSQL and Docker-based production services on EC2, would you start with the t4g.large and resize based on metrics, or is 2 vCPUs and 8 GB too tight for the database, API, and workers on one host?
I'm especially interested in CPU-credit behavior, memory usage, ARM64 compatibility, gp3 settings, whether an m7g is worth the extra cost, what thresholds you use before resizing, and whether workers should be separated from the database immediately. I'm looking for a cost-efficient starting point with reasonable production headroom, not theoretical maximum capacity.
5 Answers
A t4g.large can be a reasonable starting point for a small, mostly cached application if the workers are genuinely light and the database stays modest. Eight GB is not much once PostgreSQL, connection overhead, containers, caches, and background processes are all sharing the host, so configure memory carefully and leave headroom for bursts. Track CPU utilization, CPU credits, load average, available memory, swap activity, disk latency, IOPS, throughput, database connections, and query latency. Resize before memory regularly falls below roughly 15–20% available, swap becomes active, CPU credits trend toward zero, or storage latency and query latency rise during normal peaks.
For gp3, start with the default baseline unless measurements show a need for more IOPS or throughput. Put database data, WAL, and temporary files on storage with enough free space, enable monitoring, and avoid sizing the volume so tightly that maintenance or growth becomes stressful. Keeping workers on the same host is acceptable at this stage if their concurrency and memory use are bounded, but isolate or move them once they compete with PostgreSQL for CPU, RAM, or disk. A practical path would be to start small in a staging-like load test, use a predictable m-series size if CPU is sustained, and resize from observed metrics rather than from the service list alone.
For a database with sustained CPU usage, I’d generally prefer an m-series instance over a t-series one. T4g instances are attractive when the baseline is low and bursts are occasional, but a consistently busy PostgreSQL server can exhaust its CPU credits and become throttled. An m7g gives predictable CPU performance and usually more comfortable memory headroom. ARM64 is normally fine for PostgreSQL and common containers, but verify every image, extension, binary, and worker dependency before switching.
The bigger concern is availability rather than whether the first instance has two or four vCPUs. Keeping the database and all workers on one EC2 host creates a single failure domain. EBS is durable, but a single volume and instance do not provide high availability or protection from operator mistakes, corruption, or a regional incident. Use automated backups with point-in-time recovery, regularly test restores, and consider cross-AZ or cross-region copies. If the data is important, plan for a replica or managed database before the system grows.
The workload description alone isn’t enough to choose an instance confidently. AI processing, scheduled work, database query patterns, peak traffic, connection counts, and reliability requirements could each change the answer substantially. I’d benchmark the actual queries and jobs, then run a realistic load test before committing to a production size.

I wouldn’t jump straight to complex replication for a tiny workload, but I agree that backups need to be treated as part of the initial design. A restore test is more useful than simply seeing successful backup messages.