Portainer Docker Deployment: Web GUI for Easy Container Management

Stop using the command line for everything. Deploy Portainer on a VPS with Docker Compose to monitor and manage Docker containers via a clean web interface.

How to Self-Host Portainer on a VPS with Docker Compose

Docker Daemon Socket and Port Configuration

Portainer controls the host's Docker engine by communicating directly with the Docker daemon. This is achieved by mounting the Unix socket (/var/run/docker.sock) into the container.

The service exposes two primary ports: - 9443: Serves the web dashboard over HTTPS. - 8000: Facilitates communication between Portainer and remote Edge Agents.

Below is the production-ready docker-compose.yml deployment file:

version: '3.8'

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: always
    security_opt:
      - no-new-privileges:true
    ports:
      - "9443:9443"
      - "8000:8000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

volumes:
  portainer_data:
    driver: local

Step-by-Step Deployment Guide

Follow these steps to deploy Portainer on your VPS:

Step 1: Create a Dedicated Directory

Create a workspace to keep your Docker configurations organized:

mkdir -p ~/portainer
cd ~/portainer

Step 2: Write the Compose File

Write the YAML contents to a file named docker-compose.yml in that directory.

Step 3: Launch the Stack

Deploy the container in detached (background) mode:

docker compose up -d

Step 4: Verify the Container Status

Confirm that Portainer is running and healthy:

docker compose ps

Security Configurations

To secure a self-hosted Portainer instance, implement the following best practices:

Use a Reverse Proxy for Automatic SSL

Rather than exposing port 9443 directly to the public web, expose it locally and proxy it using Caddy or Nginx. This allows you to manage SSL/TLS certificates via Let's Encrypt automatically.

Example Caddy configuration (Caddyfile):

portainer.yourdomain.com {
    reverse_proxy https://127.0.0.1:9443 {
        transport http {
            tls_insecure_skip_verify
        }
    }
}

Configure the UFW Firewall

If you expose 9443 directly, ensure only authorized IPs can access it by using ufw:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow from YOUR_ADMIN_IP to any port 9443 proto tcp
sudo ufw enable

Clean Up Resources

To tear down the container while preserving your configurations and persistent volume, run:

docker compose down

To delete the Portainer volume and start fresh, run:

docker compose down -v