CoreMVP

VPS Deployment

Operate the Node application behind your own HTTPS reverse proxy, using Docker or a direct Node process.

See the hosted deployment status before using this deployment guide.

Use this path when you want to operate the application host yourself. Start with Vercel for managed hosting. A VPS requires you to manage the OS, firewall, HTTPS, process supervision, updates, monitoring, and recovery. Supabase still owns the configured Auth and database services.

Prepare the host

Use a maintained Linux host with Git and either Docker with Compose or the selected Node 24 deployment runtime and Bun 1.4+. The package's minimum is Node 22.23.4; this recipe uses the same Node 24 reference as the managed and container paths. Size it for your workload; a production bundle can be built on a separate machine. Give the public domain the correct DNS records, restrict SSH access, and expose only the ports your reverse proxy needs.

Install Docker using its official installation guide. For direct Node operation, follow the non-Docker path below. No app command provisions the host or applies firewall rules for you.

Run with Docker

Clone your repository, then follow Docker Deployment to create the Dockerfile, Compose configuration, public .env.build, and private .env.runtime. Use the same hosted Supabase project and the selected provider's test account throughout setup. Stripe is the default; see Polar Billing for Polar sandbox configuration. Keep provider credentials server-side.

git clone <your-repo-url>
cd your-project
docker compose up -d --build
docker compose ps
curl --fail http://127.0.0.1:3000/api/health

The documented Compose configuration binds the app to the host's loopback address. Keep that boundary when adding the reverse proxy.

Nginx configuration

Create an HTTP site for your domain before issuing its TLS certificate. For example, adapt this file to your host's Nginx layout:

/etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Enable the site using your distribution's supported layout, validate it with sudo nginx -t, and reload Nginx. Then follow Certbot's Nginx instructions for your OS to issue and renew the certificate and redirect HTTP to HTTPS. Do not expose the application port directly to untrusted clients, which could supply their own forwarded host and scheme headers.

Set NEXT_PUBLIC_APP_URL, Supabase Auth's Site URL and Redirect URLs, and the selected provider's webhook destination to this same HTTPS origin (/api/webhooks/stripe or /api/webhooks/polar). Keep prior provider credentials and webhook delivery for retained billing and outstanding work. Rebuild after changing a public setting. Check sign-in and session refresh through the public proxy; a loopback health response cannot prove TLS, cookies, or provider callbacks.

Non-Docker deployment

Install Node 24 and Bun 1.4+ from their Node and Bun instructions. Bun owns dependency installation and build commands; Node runs the production output.

Use an isolated deployment checkout. Keep local-development .env.local out of it so local values cannot override the hosted configuration. Save only public NEXT_PUBLIC_* inputs in .env.production and unprefixed server credentials in ignored .env.runtime, using the hosted reference.

bun install --frozen-lockfile
bun run build
chmod 600 .env.runtime
NODE_ENV=production HOST=127.0.0.1 PORT=3000 node --env-file=.env.runtime .output/server/index.mjs

The built server does not load environment files automatically. Run this same Node command under your host's process supervisor, with the working directory set to the deployment root and the same runtime settings. Configure restart and boot behavior in that supervisor. Keep secrets in the protected runtime file or its environment configuration, rather than command arguments.

Update and recover

Deploy reviewed source, rebuild with its public settings, and restart the owned process or container. This recipe may interrupt requests. Keep the previous accepted output or image and its matching settings for rollback. Do not reset retained database data during an application update.

Inspect the application log and /api/health first after a restart. Then check the affected Auth, content, and billing journeys. Monitor disk and memory using your host's tools. Configure database backup and restore using your Supabase plan's supported features; Git history protects source code, not runtime data.

Payment provider settings

Stripe is the default for new checkout. Set PAYMENT_PROVIDER=polar with server-only Polar settings for a Polar app; see Polar Billing. Hosted Polar on TanStack Start has not been verified. Complete the local sandbox journey and stop before live Polar activation until the hosted path is verified. Keep the app origin and Supabase public settings in the build environment. Configure public Stripe settings only for the offers you use. Missing mappings leave those offers unavailable in either mode. Preserve Stripe credentials and webhook delivery while retained billing or outstanding work still needs them.

The Node reference is 24.22.1 or later on 24.x; 22.23.4 or later on 22.x is also supported. Rebuild after public settings or the Node image change.

What's next

Was this page helpful?

On this page