Moving Off Usage-Metered Cloud (Netlify Credits & Non-Pausing Supabase Compute): Our Production Cutover Runbook to Flat-Rate Docker & PostgreSQL 16
Usage-billed frontend hosts charge credits for every production deploy, and managed cloud databases run an hourly compute meter on every project. Here is the exact step-by-step runbook we use to move a React/TypeScript + Supabase application onto a predictable flat-rate Docker + PostgreSQL 16 server.
Frequent content updates burn through monthly deploy credit allowances (15 credits per production deploy on Netlify), and running multiple small client databases adds $10–$25+/mo per database in non-pausing hourly compute fees.
Splitting a standard business web application across usage-metered edge build pipelines and per-project hourly database instances instead of running a consolidated Linux + Docker + PostgreSQL server.
Export the Supabase PostgreSQL schema and data via pg_dump, replace client-side VITE_SUPABASE_ANON_KEY calls with server-side Express + pg pool endpoints, translate netlify.toml redirects/headers into server.mjs, and deploy inside an isolated Docker container.
Interactive Netlify + Supabase to Docker + PostgreSQL Migration Checklist
Check off each step when consolidating a metered frontend + database project onto a flat-rate Linux container:
1. Moving Database Queries From the Browser Back to the Server Where They Belong
When an application is built on a serverless frontend host paired with a hosted backend-as-a-service database, the browser typically talks directly to the database REST API using a public VITE_SUPABASE_ANON_KEY. That means your security depends entirely on getting every single PostgreSQL Row-Level Security (RLS) policy right—and every production deploy or database hour ticks a usage meter.
When we migrate a site onto our Ubuntu 24.04 LTS Docker + PostgreSQL 16 platform, we simplify both the billing model and the security boundary at the same time:
• 1. No Browser-Exposed Database Keys: The React frontend no longer talks directly to a public database API. Instead, forms and portal actions send standard POST /api/... requests to the local Node.js/Express server inside the container, which validates the input, checks our 7-layer spam shield, and writes to PostgreSQL over the private internal network.
• 2. Preserving Every netlify.toml 301 Redirect: One detail people often forget when leaving Netlify is that netlify.toml redirects don’t run automatically inside a standard Node or Nginx container. We parse or port every [[redirects]] rule directly into server.mjs so not a single legacy backlink or bookmarked URL breaks during the move.
| Architecture Layer | Metered Netlify + Supabase Setup | Consolidated GreenTree Docker + PG16 Stack |
|---|---|---|
| Production Deploys | 15 credits per production deploy on credit-based plans | Unlimited deploys included on your flat monthly hosting plan |
| Database Compute Clock | Hourly compute billed 24/7 per active paid database instance | Dedicated PostgreSQL 16 database included on the server |
| Database Access Boundary | Public REST endpoint + VITE_ anon key in browser JS | Private server-side pg.Pool (port 5432 never exposed to public) |
| 301 Redirects & Headers | Configured in proprietary netlify.toml syntax | Enforced natively in Express server.mjs + reverse proxy |
2. Exporting Clean Public Tables From Supabase Into Standard PostgreSQL 16
Because Supabase is built on PostgreSQL, your application data is never trapped—you just need to export it cleanly. If you run a naive full-cluster pg_dump against a Supabase connection string, the dump will include Supabase-specific schemas (auth, storage, realtime, supabase_functions) and role ownership statements that will throw errors when restored onto a standard self-hosted PostgreSQL 16 server.
The secret to a clean migration is passing --schema=public --no-owner --no-acl to pg_dump. That exports only your application’s public tables, indexes, sequences, and rows as portable SQL that imports cleanly into any standard PostgreSQL 16 database in seconds.
# 1. Export only the public application schema & data (stripping cloud-specific role ownership) pg_dump "postgresql://postgres.<project-ref>:<password>@aws-0-us-east-1.pooler.supabase.com:5432/postgres" \ --schema=public \ --no-owner \ --no-acl \ --format=custom \ --file=app_public_migration.dump # 2. Create the dedicated single-tenant database and role on your target PostgreSQL 16 server psql -U postgres -c "CREATE ROLE app_user WITH LOGIN PASSWORD 'strong_random_password';" psql -U postgres -c "CREATE DATABASE app_prod OWNER app_user;" # 3. Restore cleanly into the new PostgreSQL 16 database and verify row counts pg_restore --no-owner --no-acl --role=app_user -d app_prod app_public_migration.dump psql -U app_user -d app_prod -c "SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;"
Common Questions on Migrating Off Metered Netlify & Supabase to Flat-Rate Docker
Can I still use Git-push deployments after moving off Netlify to your Docker cloud?
Yes. Every application container on our platform is wired to a Git webhook build pipeline: when we push a commit to main, the server builds the TypeScript/React bundle, prerenders all static HTML routes, runs our 8-point security check, and swaps the container with zero per-deploy credit fees.
What if my app grows and starts using heavy CPU, RAM, or database resources?
Our $99/mo and $199/mo plans are flat-rate—meaning we never send surprise usage overage invoices—and they are sized for normal commercial websites and business applications. We actively monitor CPU, memory, disk, database load, and bandwidth across every container so no single $99/mo account can consume all the server’s resources, and if a site grows to use a disproportionate share, we work directly with you to upgrade to a $299+/mo Dedicated Environment or $599+/mo Cluster.
Why do you pass --no-owner and --no-acl when running pg_dump from Supabase?
Supabase attaches internal roles (such as supabase_admin, anon, authenticated, and service_role) to database objects. When moving to a server-side Express + PostgreSQL architecture where the database is accessed only by your backend application user, --no-owner and --no-acl strip those cloud-specific role grants so the schema imports cleanly.
Why Your Website Bill Should Not Depend on How Often It Is Used: Usage-Billed Cloud vs. Flat-Rate Hosting
Read the full 4,000+ word pricing and architecture comparison of usage-metered cloud platforms versus our flat-rate $99–$199/mo hosting (with active resource monitoring).
Ready to Move Your Website or App Onto Predictable $99–$199/mo Hosting?
We handle the complete migration off metered cloud platforms or legacy hosts into an isolated Docker + PostgreSQL 16 environment on our High-Availability Linux cloud—with 1 SmarterMail Enterprise mailbox included and active resource stewardship.
Related Notes From the Sysadmin Desk
The Zero-Downtime Server Transfer Runbook: How We Move Live Websites, Databases, and Mailboxes Without Losing a Single Order or Email
Execute a 5-stage cutover: lower DNS TTLs to 60 seconds 48 hours early, stage and test via local hosts-file override, pre-issue TLS certificates, run a final sub-second rsync + database delta sync, and enforce a 5-source 301 redirect map.
Locking the Front Door on Modern Web Apps: Why Your Production Container Should Never Serve From the Project Root (Plus Our 8-Point Pre-Launch Gate)
Enforce strict path.resolve(DIST_DIR) confinement with explicit 403/404 dotfile blocking, disable production .map files, keep secrets strictly server-side, and run an 8-point negative-testing gate before launch.
Writable Standby PostgreSQL Failback Merges: Why ON CONFLICT (id) DO NOTHING Silently Drops Rows on Sequential IDs (And How to Fix It With UUIDs or Split Sequence Ranges)
Audit every table in the failback merge list: use UUID primary keys (DEFAULT gen_random_uuid()) on append-only lead/submission tables, or configure non-overlapping odd/even sequence steps (INCREMENT BY 2) dynamically above MAX(id)—and check secondary unique constraints and payload hashes before skipping.