Module C-1·8 min read

Deploy a full stack (Next.js, Node, Postgres, Redis, Nginx) to a Linux VPS with production-ready security and operations.

Written by Jatin Jain Saraf · Architect of SupraScan

Capstone: Deploying a SaaS Application

Introduction

You have mastered container internals, optimized your multi-stage builds, secured your runtime environments, and configured robust observability.

Now it is time to put everything together.

In this final capstone module, we will architect a production-ready deployment for a full-stack SaaS application on a Linux Virtual Private Server (VPS).


The SaaS Architecture

We will deploy a complete modern stack:

  1. Nginx: A reverse proxy handling SSL termination and routing.
  2. Next.js: The frontend client.
  3. Node.js (Express): The backend API.
  4. PostgreSQL: The primary relational database.
  5. Redis: The session cache and job queue.

Deployment Constraints

This deployment must meet strict production criteria:

  • No root users running application code.
  • Minimal image sizes via multi-stage builds.
  • Isolated networking (the database cannot be accessible from the internet).
  • Guaranteed boot order via healthchecks.
  • Resource limits to prevent host crashes.
  • Secret management to avoid leaking database passwords.

Step 1: The Optimized Dockerfiles

You will maintain separate Dockerfiles for the Frontend and the Backend.

The Backend (Node.js API)

# api/Dockerfile
# syntax=docker/dockerfile:1
 
# -- STAGE 1: Dependencies --
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
 
# -- STAGE 2: Builder --
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
 
# -- STAGE 3: Production Runner --
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
 
# Install dumb-init for proper signal handling (PID 1)
RUN apk add --no-cache dumb-init
 
# Copy minimal artifacts
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/node_modules ./node_modules
 
# Security: Switch to non-root user
USER node
 
# Healthcheck: Fail if API cannot answer a ping
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
 
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "dist/server.js"]

Step 2: The Production Compose File

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...