+44 7575 472931[email protected]
HostAccentKnowledge BaseHosting, websites, SEO, and growth

Install Docker on Ubuntu VPS for Production: Clean and Secure Setup

Production-ready Docker installation tutorial for Ubuntu VPS, including non-root setup, firewall, compose health checks, and maintenance routine to keep your VPS clean.

Linux HostingVPSServer Management
Install Docker on Ubuntu VPS for Production: Clean and Secure Setup - Linux Hosting guide cover image

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

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.

Reviewed by

HostAccent Editorial Team · Editorial Team

Last updated

Apr 12, 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.

How do I choose the right VPS location for my audience?

Pick the datacenter closest to your primary users, then test latency, page speed, and checkout flow from that region before scaling.

When should I move from shared hosting to VPS?

Move when you need guaranteed resources, root-level control, custom server tuning, or when traffic spikes cause unstable performance.

What baseline security should a new VPS have?

Use strong SSH practices, firewall rules, auto security updates, regular backups, and active monitoring for uptime and suspicious activity.

Start typing to find the right article.

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