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:
Nginx: A reverse proxy handling SSL termination and routing.
Next.js: The frontend client.
Node.js (Express): The backend API.
PostgreSQL: The primary relational database.
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)
dockerfile
Step 2: The Production Compose File
We will orchestrate the entire deployment using a production-hardened compose.yaml.
yaml
Step 3: Analyzing the Architecture
Why did we build it this way?
1. Isolated Networking
Notice that the db and cache services do not have a ports directive.
They are completely inaccessible from the public internet. The only way to talk to PostgreSQL is to be on the saas_net internal bridge network.
Similarly, the api service does not publish port 8080. Instead, Nginx binds to ports 80 and 443 on the host, accepts public internet traffic, handles SSL, and proxies the traffic internally over saas_net to the API container.
2. Log Management
We explicitly limited the Node.js API logs to max-size: "50m" with 3 rolling files. If the API gets hit by a massive spike in traffic, it will not fill up the Linux VPS hard drive and crash the server.
3. Graceful Boot Sequencing
By combining the pg_isready healthcheck with depends_on: condition: service_healthy, Compose guarantees that the Node.js API will not boot until the database is fully ready to accept queries.
Step 4: The Deployment Workflow
If you SSH into your Linux VPS, your deployment workflow is simple, repeatable, and completely declarative:
Create a .env file containing the DB_PASS.
Create the ./secrets/db_password.txt file.
Run the deployment:
bash
Docker will build the minimal multi-stage image, create the internal networks, provision persistent volumes, boot the database, wait for it to become healthy, boot the API, and finally boot the Nginx proxy.
Conclusion
Congratulations. You have completed the Docker In-Depth curriculum.
You are no longer a developer who just memorizes docker run. You understand how the Linux kernel provides namespaces and cgroups. You know how OverlayFS layers dictate caching strategy. You understand the dangers of PID 1, the risks of running as root, and how to orchestrate complex services securely.
You are now equipped to engineer, secure, and operate Docker containers in high-stakes production environments.
Knowledge Check
In the production compose.yaml file presented in this module, neither the db (PostgreSQL) nor the api (Node.js) services have a ports directive defined. How do external users interact with the application?
In the provided multi-stage Dockerfile for the Node.js API, why is the dumb-init package installed via apk add?
What is the primary purpose of defining limits under the deploy.resources key for the db, cache, and api services in the production Compose file?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.