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 Dockerfile or docker-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.