Overview
Docker makes it trivial to package and ship applications, but getting a container running in production on a VPS — with a proper domain, HTTPS, and a reverse proxy — involves a few more steps than docker run. This guide walks you through the full deployment workflow: from installing Docker to serving your containerized app securely over HTTPS with Nginx.
What You'll Need
- A VPS running Ubuntu 22.04+ (with root or sudo access)
- A domain name pointed at your server's IP (A record)
- A Dockerized application (with a
Dockerfileordocker-compose.yml)
Step 1: Install Docker and Docker Compose
Use Docker's official install script for the most reliable setup:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
Verify the installation: docker --version
Install the Compose plugin: sudo apt install docker-compose-plugin -y
Step 2: Deploy Your Application Container
Create a working directory and drop in your docker-compose.yml. A minimal example for a Node.js app:
services:
app:
image: your-dockerhub-user/your-app:latest
restart: always
expose:
- "3000"
Note the use of expose rather than ports — this makes port 3000 available internally on the Docker network but not publicly. Nginx will handle public traffic.
Start the container: docker compose up -d
Step 3: Install and Configure Nginx
sudo apt install nginx -y
sudo systemctl enable nginx
Create a server block for your domain:
sudo nano /etc/nginx/sites-available/myapp
Paste the following configuration:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the site and test the config:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 4: Add HTTPS with Let's Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will automatically modify your Nginx config to handle HTTPS and set up a cron job for auto-renewal. Verify renewal works: sudo certbot renew --dry-run
Step 5: Open Firewall Ports
sudo ufw allow 'Nginx Full'
sudo ufw reload
This opens ports 80 and 443. Your application port (3000) should remain closed to the public — only Nginx needs to reach it internally.
Step 6: Set Up Automatic Container Restarts
The restart: always directive in your Compose file ensures Docker restarts your container if it crashes or the server reboots. Verify it's running after a test reboot: docker compose ps
Updating Your Application
When you push a new image, update the deployment with:
docker compose pull
docker compose up -d --remove-orphans
Zero-downtime deployments can be achieved by adding a load balancer or using Docker Swarm / Kubernetes for more complex setups.
You're Live
You now have a production-grade Docker deployment: containerized app, Nginx reverse proxy, automatic HTTPS, and container restart on failure. This pattern scales well — add more services to your Compose file as your application grows.