Skip to main content
+44 7575 472931[email protected]
HostAccentKnowledge BaseHosting, websites, SEO, and growth

Install Docker on Ubuntu VPS: Secure Production Setup

Install Docker on an Ubuntu VPS for production: the right repo, non-root setup, firewall rules, Compose health checks, and a clean maintenance routine.

Linux HostingVPSServer ManagementDevOps
Installing Docker on Ubuntu VPS – secure production container setup guide

Before you install Docker Ubuntu VPS setups need a production-safe path, not the fast one. A lot of Docker tutorials show you the fast path: curl | bash, done. That's fine for a local machine. On a production VPS, it leaves you with Docker running as root, open ports you didn't intend to expose, and no cleanup routine — meaning your disk fills up silently over time.

This guide does it properly. It takes about 10 extra minutes compared to the lazy install, and it saves you from the kind of problems that show up at 2am.

Before you start

This guide uses Ubuntu — the best Linux distro for most VPS workloads — though the steps adapt cleanly to Debian or AlmaLinux.

You need:

  • Ubuntu 22.04 or 24.04 LTS VPS
  • A non-root sudo user (never run this as root)
  • SSH access and a basic UFW firewall in place

If your VPS is brand new, do this first:

bash
sudo apt update && sudo apt upgrade -y
sudo adduser deploy
sudo usermod -aG sudo deploy

Then reconnect as deploy before continuing.

Step 1: Install Docker from the official repository

Don't use the Ubuntu snap version or apt install docker.io — both are outdated. Install from Docker's own repo:

bash
sudo apt update
sudo apt install ca-certificates curl gnupg -y
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

Verify it installed:

bash
docker --version
docker compose version

Step 2: Run Docker without root

Running containers as root is a security risk. Add your user to the docker group:

bash
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world

If hello-world runs successfully without sudo, you're set. If you still need sudo, log out and back in — group membership needs a fresh session.

Step 3: Enable Docker on reboot

By default Docker may not start automatically after a server restart:

bash
sudo systemctl enable docker
sudo systemctl enable containerd
sudo systemctl status docker --no-pager

Status should show active (running). Test this properly by actually rebooting your VPS once and confirming Docker comes back up:

bash
sudo reboot
# after reconnecting:
docker ps

Step 4: Firewall rules — keep ports minimal

Here's where most guides skip something important: Docker can bypass UFW rules. By default, Docker modifies iptables directly. If you docker run -p 8080:80, that port is publicly accessible even if UFW says it's blocked.

The safest approach for a production VPS: put Nginx in front and only expose 80 and 443. Don't bind container ports directly to public interfaces unless you specifically need to.

bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

For containers that don't need public access (databases, caches), bind only to localhost:

yaml
ports:
  - "127.0.0.1:5432:5432"  # PostgreSQL — local only

Step 5: A compose file with health checks

Health checks tell Docker whether your container is actually working, not just running. Add this to any compose.yml:

yaml
services:
  app:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

restart: unless-stopped means the container comes back after a VPS reboot or a crash — unless you explicitly stopped it yourself.

Start it:

bash
docker compose up -d
docker ps

Look for (healthy) in the STATUS column. If you see (health: starting), wait 30 seconds and check again.

Step 6: Nginx reverse proxy in front of Docker

Run Nginx on the host (not in Docker) to handle SSL and route traffic:

bash
sudo apt install nginx -y

/etc/nginx/sites-available/myapp:

nginx
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
bash
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Then add SSL via Certbot:

bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com

Step 7: Maintenance routine — keep your VPS clean

Docker is notorious for quietly filling up disks. Old images, stopped containers, unused volumes — they accumulate. Add a weekly cleanup:

bash
# See what Docker is using:
docker system df

# Remove stopped containers, unused images, dangling volumes:
docker system prune -f

# More aggressive — removes all unused images (not just dangling):
docker image prune -a -f

Set a cron job to run this weekly:

bash
crontab -e
# Add:
0 3 * * 0 docker system prune -f >> /var/log/docker-prune.log 2>&1

This runs every Sunday at 3am. Adjust to suit your schedule.

Common mistakes to avoid

Running everything as root. If your container gets compromised, root access means the attacker has your whole server.

Exposing database ports publicly. MySQL on port 3306, Redis on 6379 — these should never be publicly accessible. Bind to 127.0.0.1 only.

No restart policy. Without restart: unless-stopped, a container crash or server reboot leaves your app offline until you notice.

Skipping the prune routine. On a 40GB VPS, it takes a few months of Docker use to quietly fill the disk. By the time you notice, it's causing errors.

Quick FAQ

Should I run Nginx inside Docker or on the host?

For most VPS setups, running Nginx on the host and your app(s) in Docker is simpler to manage. It avoids Docker networking complexity and makes SSL via Certbot straightforward. The containerized Nginx approach makes more sense when you're orchestrating many services across multiple servers.

How do I update a running container?

bash
docker compose pull
docker compose up -d

This pulls the latest image and recreates containers with zero downtime if your app supports it.

Docker or Docker Desktop?

Docker Desktop is for local development on macOS/Windows. Your VPS runs Docker Engine (what we installed here). They're different products.

You're ready

A properly installed Docker setup on Ubuntu VPS — from the right repository, with non-root access, sensible firewall rules, health checks, and a cleanup routine — is solid production infrastructure that'll run reliably without surprises.

Need a clean Ubuntu VPS to run this on? HostAccent VPS plans come with Ubuntu pre-installed and ready to go.

Last updated

Jun 20, 2026

HostAccent Editorial Team publishes practical hosting guides, operations checklists, and SEO-focused tutorials for businesses building international web presence.

Discussion

Have a question or tip about this topic? Share it below — your comment will appear after review.

Your email stays private and is only used for moderation.

Write for the Community

Have a tutorial, tip, or insight to share? Get published on the HostAccent Blog with your name, bio, and website link.

Become a Contributor

Need a faster setup for this workflow?