Saturday, August 1, 2026 · Updated September 24, 2026

How To Deploy A Full-Stack App With PostgreSQL To A VPS

Deploy a full-stack web application and PostgreSQL database to a VPS

Repeat this deployment from your terminal

QuickDeploy builds your app, runs it in Docker, and sets up HTTPS on your own Ubuntu VPS.

€15 once, with updates included. VPS and domain costs are separate.

Deploying a full-stack application to your own VPS usually means configuring several moving parts: building the app, running PostgreSQL, connecting both containers, setting up a reverse proxy, and enabling HTTPS.

This guide shows how to deploy the application and database with QuickDeploy. The final setup connects the app and PostgreSQL through QuickDeploy's shared Docker network, persists the database on the server, and publishes only the web application through your domain. PostgreSQL is not published as a host port.

If you want to understand the underlying container and reverse-proxy steps first, read the Docker VPS deployment guide.

What You Will Deploy

The deployment contains:

  • Your full-stack application in a Docker container.
  • PostgreSQL in a separate Docker container.
  • QuickDeploy's shared Docker network connecting the application and database.
  • A persistent directory for PostgreSQL data.
  • Caddy as the HTTPS reverse proxy for your domain.

QuickDeploy creates and manages these resources on your VPS. You still own the server, Docker configuration, application, and data.

Prerequisites

Before starting, make sure you have:

  • An Ubuntu-based VPS with Docker and Docker Compose installed.
  • A domain or subdomain with an A record pointing to the VPS.
  • QuickDeploy installed and configured.
  • A full-stack application that can read its database connection from an environment variable.
  • A production start command for the application.

If your server is not ready yet, follow the Linux VPS setup guide.

Verify The Fit Before Buying

You can check the local tool and your server before purchasing a license:

  1. Compare your computer, Ubuntu-based VPS, application, database, and domain with the compatibility matrix.
  2. Install QuickDeploy for your platform and run quickdeploy --version. This local check does not require a license or connect to your server.
  3. Run the server verification command in the requirements guide to confirm that the SSH user can reach Docker and Docker Compose without an interactive sudo prompt.

If those checks pass and your application reads DATABASE_URL from the environment, return to the deployment command below. A license is required when you configure QuickDeploy and deploy, not for the local version check.

Try the Next.js and Prisma sample

The downloadable Next.js/PostgreSQL example includes Next.js 16.3.6, Prisma 6.19.3, a committed migration, a form that writes a note, and a health route that queries the database. This is a deployment demonstration: its notes are public and it has no authentication. Use only disposable test data. Add access controls before adapting it for real users.

The app starts in three stages: wait for a database connection, run prisma migrate deploy, then start Next.js on port 3000. A connection or migration failure stops startup. The sample keeps Prisma CLI available in the runtime because migration execution needs it. It builds the Prisma client while building the image; migrations run later, against the actual runtime database.

To try it on your computer with Docker, extract the archive and create compose.yaml in that directory:

services:
  database:
    image: postgres:15
    environment:
      POSTGRES_USER: demo
      POSTGRES_PASSWORD: local-example-only
      POSTGRES_DB: demo
    volumes:
      - database_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U demo -d demo"]
      interval: 2s
      timeout: 3s
      retries: 30
  app:
    build: .
    ports:
      - "127.0.0.1:3100:3000"
    environment:
      DATABASE_URL: postgresql://demo:local-example-only@database:5432/demo
      RELEASE_ID: first-deploy
    depends_on:
      database:
        condition: service_healthy
volumes:
  database_data:

These credentials are for this isolated local example only. Use a free local port if 3100 is occupied and choose a unique Compose project name:

docker compose -p my-postgres-recipe config --quiet
docker compose -p my-postgres-recipe up --build -d
curl --fail http://localhost:3100/api/health

Open http://localhost:3100, save a test note, and confirm it appears. Change the Compose RELEASE_ID to second-deploy and run docker compose -p my-postgres-recipe up -d app. The health route should report the new release and the saved note must still exist. This checks a database operation across an app replacement, rather than merely checking that the database container is running.

To use the source with QuickDeploy, use the credentials and myapp-postgres hostname described below in .env.production, not the local database hostname. The included nixpacks.toml selects Node.js 24 and OpenSSL explicitly from a pinned package archive. QuickDeploy uses that Nixpacks configuration; it does not build the sample Dockerfile or use the local Compose file.

See the Next.js deployment recipe for app configuration, HTTPS and update checks. For a first QuickDeploy deployment use a suitable test server: QuickDeploy manages a shared Caddy service and can recreate that proxy. Do not run it on a host where existing containers must stay untouched.

Choose A Project Name

QuickDeploy uses the project name to keep related containers and data together. This guide uses myapp.

The PostgreSQL container will therefore be reachable inside Docker as:

myapp-postgres

Use the same project name for future deployments so QuickDeploy updates the existing application instead of creating another project.

Configure The Database Connection

Create a .env.production file in your application root. QuickDeploy includes this file when building the app and makes it available to the running container.

DATABASE_URL=postgresql://quickdeploy_app:YOUR_RANDOM_PASSWORD@myapp-postgres:5432/quickdeploy_app

Replace YOUR_RANDOM_PASSWORD with a long, unique hexadecimal password. A 64-character value is a good default; on macOS or Linux, you can generate one with:

openssl rand -hex 32

Use the exact same value in the deployment command below. Hexadecimal output avoids shell-sensitive characters when QuickDeploy forwards the deployment flags over SSH.

The database hostname is myapp-postgres, not localhost. The app and database run in separate containers, so localhost inside the application container refers only to the application container itself.

Do not commit .env.production to Git. Add it to .gitignore if it is not already ignored:

.env.production

Check The Application Port

QuickDeploy needs to know which port the application listens on inside its container. For example, a typical Next.js application listens on port 3000.

Make sure the application listens on 0.0.0.0, not only 127.0.0.1. Keep the downloadable sample's complete nixpacks.toml, including its pinned Node.js setup. For other applications, a custom Nixpacks start command can be configured when framework detection does not choose the correct command:

[phases.build]
cmds = ["npm run build"]

[start]
cmd = "npm run start"

See the runtime configuration documentation for more information.

Deploy The App And PostgreSQL

From the application directory, run:

quickdeploy push \
  --domain app.example.com \
  --project myapp \
  --port 3000 \
  --db postgres \
  --db-port 5432 \
  --db-user quickdeploy_app \
  --db-password 'YOUR_RANDOM_PASSWORD' \
  --db-name quickdeploy_app

Change:

  • app.example.com to your domain.
  • 3000 to the port used by your application.
  • YOUR_RANDOM_PASSWORD to the password from .env.production.

QuickDeploy uploads the project, builds the application image on the VPS, creates PostgreSQL when it does not already exist, starts the containers, checks that the application container remains running, and configures Caddy for the domain.

Verify The Deployment

Open your domain after the command finishes:

https://app.example.com

HTTPS certificate provisioning can take a short moment during the first deployment.

QuickDeploy's container check is process-based; it does not request an HTTP health endpoint. After deployment, verify the public URL and an application route that reads from PostgreSQL.

If the application starts but cannot reach PostgreSQL, check these common causes:

  1. The project name in the command must match the hostname in DATABASE_URL.
  2. The database user, password, and database name must match in both places.
  3. The application must use myapp-postgres, not localhost, as its database host.
  4. The application must listen on the port passed through --port.

You can inspect the project containers on the server with:

docker compose -f ~/myapp/docker-compose.yml ps

Run Database Migrations

Commit reviewed migration files alongside the application. In this example, the first migration creates the Note table. Production applies pending files with prisma migrate deploy; do not run development migration generation or prisma migrate reset against production data.

The sample runs migrations before starting its HTTP server. This is useful for a small single-app example, but migrations in a rolling release must remain compatible with the old app that is still serving traffic. Add new columns/tables first, deploy compatible code, and remove obsolete schema only in a later release. For larger deployments, coordinate migration execution in a dedicated release step. A previous app image does not reverse a database migration.

Before changing an existing database, take a backup and test the migration on a restored copy. Do not expose a migration HTTP endpoint. If migration execution fails, inspect the migration state and repair deliberately before starting the new app. Keep the Prisma CLI and migration directory in whichever image or release job executes the command.

Deploy Updates

Use the same command when shipping a new version. QuickDeploy builds the new application container, checks that it stays running, switches Caddy to it, and then removes the previous application container.

The PostgreSQL service and its data directory remain part of the project instead of being recreated for every application update.

Back Up PostgreSQL

QuickDeploy stores the PostgreSQL data below the project directory on your VPS:

~/myapp/data/postgres

Application deployment does not replace this directory. You should still create regular off-server backups. Also back up the database before removing the project, because removing a deployment can delete its project files and database data.

For the project name and database user used in this guide, run on the VPS:

umask 077
docker exec myapp-postgres pg_dump -U quickdeploy_app -d quickdeploy_app -Fc > myapp-backup.dump
test -s myapp-backup.dump
docker exec -i myapp-postgres pg_restore --list < myapp-backup.dump

The list command checks that the archive can be read; it is not a restore test. Copy the backup off the VPS and rehearse a full restore into a separate disposable database, then test the application against it. Do not restore over production as a tutorial exercise. Schedule backups independently; QuickDeploy does not schedule them for you.

PostgreSQL On A VPS: Production FAQ

Can The Application And PostgreSQL Run On The Same VPS?

Yes. A single VPS is a practical starting point for small applications and teams that value a simple deployment and direct infrastructure control. The application and database still run in separate Docker containers, but they share the server's CPU, memory, disk, and failure domain. Monitor those resources as usage grows.

Is PostgreSQL Exposed To The Public Internet?

Not by the generated QuickDeploy configuration. PostgreSQL exposes port 5432 only to containers on the shared Docker network; it does not publish that port on the VPS host. Your application connects to the internal hostname myapp-postgres, while Caddy publishes only the web application through HTTPS.

This network isolation does not replace normal server maintenance. Keep Docker, PostgreSQL, and the operating system updated, and restrict administrative access to the VPS.

Where Does The Database Data Persist?

PostgreSQL writes to ~/myapp/data/postgres on the VPS. Re-deploying the application with the same project name preserves that directory and reuses the existing database service.

What Happens To PostgreSQL During An Application Update?

QuickDeploy starts the new application container and checks that its process remains running before switching traffic. It does not recreate an existing PostgreSQL service during that application update, so the database and its persistent directory stay in place. Verify the public application after each deployment because this process check does not call an HTTP endpoint.

Does QuickDeploy Back Up PostgreSQL Automatically?

No. QuickDeploy persists the database on the VPS, but backup scheduling and recovery testing remain your responsibility. Keep regular backups outside the VPS so a server or disk failure does not remove the application and its only backup together.

When Should I Use A Managed Database Instead?

Use a managed PostgreSQL service or a separate database server when you need features such as managed backups and point-in-time recovery, automated failover, stricter compliance controls, independent scaling, or stronger failure isolation. A database on the application VPS is a better fit when simplicity, cost control, and infrastructure ownership matter more and you are prepared to operate monitoring and backups.

Deploy Without Giving Up Your Server

This setup gives you a conventional Docker and PostgreSQL deployment without requiring a hosted application platform. You can inspect the generated Compose file, connect other services, move the project, or manage the containers yourself.

QuickDeploy packages the repetitive deployment steps into one command while keeping the application and data on infrastructure you control.

Get QuickDeploy and deploy your first full-stack application.

Make your next deployment repeatable

Keep your VPS and use QuickDeploy to upload and build your app, configure its container, and set up HTTPS. After configuration, deploy updates with quickdeploy push.

Bring an Ubuntu VPS with Docker, Compose, and SSH access. You manage the server, backups, and application health.

One-time license · Updates included · 14-day money-back guarantee. Hosting and domain fees are separate.

Simple pricing

Pay once. Keep shipping.

QuickDeploy license

A lifetime license for the QuickDeploy CLI, with updates, fixes, and improvements included.

Use your own VPS and domain. Hosting and domain fees are paid separately to your providers.

Have an Ubuntu VPS with Docker, Compose, and SSH access? Check compatibility before buying.

After purchase, install the CLI and run quickdeploy configure with your purchase email or license key, then deploy from your project directory. View installation instructions.

You can download the CLI and check its version before buying. Configuration and deployment require a license.

What's included

  • One-time payment, no QuickDeploy subscription
  • CLI for macOS, Linux, and Windows
  • Updates and improvements included
  • Deploy to your own server

One-time payment

€15EUR

For your own Ubuntu VPS with Docker and SSH. Hosting and domain fees are separate.

Buy QuickDeploy — €15

Opens Stripe checkout in a new tab.

14-day money-back guarantee.
No questions asked.

Built by Maximilian Kürschner. Unsure whether your app fits? Ask about your setup with your framework, server OS, and database.

How To Deploy A Full-Stack App With PostgreSQL To A VPS