Skip to main content
How to Host Docker on VPS in 2025 - Virtarix Blog

How to Host Docker on a VPS

October 3, 2025 · Blog / Technical Guides

Docker solves a problem every developer faces: “It works on my machine, but not in production.”

It packages your application with everything it needs into a lightweight container. Think of it as a shipping container for your code, running the same way whether it is on your laptop, a staging server, or a production VPS.

Containers share the host’s operating system kernel instead of spinning up entire virtual machines. As a result, you can run five to ten applications on a 4 GB VPS where two traditional VMs would struggle.

Step-by-step to run an Nginx container

  1. Open your terminal and run this command:
Run Nginx in a detached container on port 80
 docker run -d -p 80:80 nginx
  1. What happens next:
  • Docker pulls the official Nginx image from Docker Hub.
  • It creates a container in detached mode (-d).
  • It maps port 80 of the container to port 80 of your VPS so you can visit the site in a browser.

This single command downloads Nginx, creates a container, and serves your website. No complex installation, no configuration conflicts, no headaches.

Prerequisites

Skip the bare minimums that barely work and start with specifications that keep your environment stable.

Component Minimum Recommended Why this matters
RAM 2 GB 4 GB+ Docker uses about 300 MB; each container needs 50–500 MB
Storage 20 GB SSD 40 GB+ NVMe Images pile up fast, logs grow, you need room to breathe
CPU 2 vCPU 4+ vCPU Container startup and build processes are CPU-intensive
OS Ubuntu 24.04+ Ubuntu 24.04 LTS Latest Docker features and best security patches

Critical requirements

  • KVM or VMware virtualization (avoid OpenVZ)
  • Outbound internet on ports 80/443 for image pulls
  • Root or sudo access for installation

That 1 GB VPS might seem tempting, but you will hit memory limits the moment you try to build anything substantial. Start with 4 GB, your future self will thank you.

Step 1: connect and verify your VPS

Alright, let’s get you connected to your server. I’ll assume your VPS provider gave you SSH credentials.

If you are still using password authentication in 2025, we can fix that later, but let’s start with the basics. First, connect to your VPS by running:

Connect to the VPS over SSH
ssh username@your_server_ip

Replace username and your_server_ip with the actual credentials your provider gave you. If you are on Windows and somehow do not have SSH built in, you can use a tool like PuTTY.

Now we need to make sure your system is ready for Docker, and to check if your kernel version is at least 4.0 (5.15 or newer is ideal), run:

Show the running Linux kernel version
uname -r

To verify your kernel version is 4.0 or higher:

Check whether the kernel major version is at least 4
if [ $(uname -r | cut -d. -f1) -ge 4 ]; then echo "Kernel OK"; else echo "Kernel too old"; fi

To verify your operating system version is Ubuntu 22.04 or newer, run:

Show operating-system version fields
cat /etc/os-release | grep VERSION

To confirm you have at least 2 GB of available memory, run:

Show a human-readable memory summary
free -h

To be sure you have at least 20 GB of free disk space on the root partition, run:

Show free space on the root filesystem
df -h /

Finally, to check that your VPS uses KVM or VMware virtualization (not OpenVZ), run:

Show the virtual system product name
sudo dmidecode -s system-product-name

You should see:

  • Kernel version 4.0 or higher (5.15+ is best)
  • Ubuntu 22.04 or newer
  • At least 2 GB of RAM available
  • 20 GB or more of free disk space
  • Virtualization type showing KVM or VMware

If any of these checks fail, stop here and contact your VPS provider before moving on.

Step 2: clean installation (skip the headaches)

Here is something we wish someone had told us years ago: always clean out any old Docker remnants before installing. Leftover packages can create strange conflicts later.

First, remove any existing Docker-related packages by running:

Remove legacy Docker packages and unused dependencies
sudo apt-get remove docker docker-engine docker.io containerd runc -y
sudo apt autoremove -y

Do not worry if you see messages like “package not found.” That is actually what you want—it means there is nothing old to interfere.

Next, update your system so you have the latest security patches and kernel updates before installing Docker:

Update all Ubuntu packages
sudo apt update && sudo apt upgrade -y

This step might take a few minutes, but it is essential to avoid problems later.

Step 3: install Docker (the right way)

For a production server in 2025, it is best to follow Docker’s official installation method instead of using the quick convenience script that is meant only for development.

First, set up Docker’s official APT repository. To add Docker’s GPG key, run:

Install Docker repository prerequisites and signing key
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Next, add the repository to your APT sources:

Attempt to add Docker's Ubuntu apt repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] 
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | 
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Now install Docker and all required components:

Install Docker Engine and Compose plugins
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Start and enable the Docker service so it runs automatically:

Start enable and inspect the Docker service
sudo systemctl start docker
sudo systemctl enable docker
sudo systemctl status docker

You should see active (running) in green. If not, check the logs:

Show Docker service journal entries
sudo journalctl -u docker --no-pager

To run Docker commands without sudo, add your user to the docker group:

Add the current user to the docker group
sudo usermod -aG docker $USER

Log out and log back in for group changes to take effect, or run:

Start a shell with the docker group active
newgrp docker

Finally, verify that everything is working:

Show Docker's version and run the hello-world image
docker --version
docker run hello-world

If you see Hello from Docker! you are all set. If not, we will troubleshoot in the next section.

Secure your firewall rules

Make your Docker firewall rules persistent so they survive reboots:

Attempt to persist the current iptables rules
## Make iptables rules persistent
sudo apt install iptables-persistent
sudo iptables-save > /etc/iptables/rules.v4

Step 4: essential Docker commands (your daily toolkit)

These are the commands we use every single day. Master them and you will feel at home with Docker.

Container management

To run a container:

Run a detached Nginx container named myapp
docker run -d --name myapp nginx

To list running containers:

List running Docker containers
docker ps

To list all containers, including stopped ones:

List all Docker containers
docker ps -a

To stop a container:

Stop the myapp container
docker stop myapp

To start it again:

Start the myapp container
docker start myapp

To remove a container:

Remove the myapp container
docker rm myapp

Image management

To pull an image:

Pull the Nginx 1.25 image
docker pull nginx:1.25

To list images:

List local Docker images
docker images

To remove an image:

Remove the local Nginx 1.25 image
docker rmi nginx:1.25

To build an image from a Dockerfile:

Build the myapp image from the current directory
docker build -t myapp .

Debugging and monitoring

To view container logs:

Show logs from the myapp container
docker logs myapp

To follow logs in real time:

Follow logs from the myapp container
docker logs -f myapp

To execute commands inside a container:

Open an interactive Bash shell in myapp
docker exec -it myapp /bin/bash

To monitor resource usage:

Monitor live Docker container resource usage
docker stats

Cleanup (use carefully)

To remove stopped containers:

Prune stopped Docker containers
docker container prune

To remove unused images:

Prune dangling Docker images
docker image prune

For a full cleanup of everything unused:

Safety check: Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

Prune all unused Docker resources and images
docker system prune -a

Pro tip: Always give your containers descriptive names, such as --name web-frontend instead of leaving them with random IDs. Your future self, especially at 2 AM, will thank you.

Step 5: Docker Compose (where the magic happens)

This is where Docker becomes truly powerful. Instead of managing individual containers, you can define your entire application stack in a single file.

Docker Compose is already installed from the previous step. To verify it works, run:

Show the Docker Compose version
docker compose version

Now create your first docker-compose.yml file with the following content:

Define an Nginx and MySQL Compose stack
version: '3.8'
services:
  web:
    image: nginx:1.25
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    restart: unless-stopped
  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: your_secure_password
      MYSQL_DATABASE: myapp
    volumes:
      - mysql_data:/var/lib/mysql
    restart: unless-stopped
volumes:
  mysql_data:

To launch your stack, run:

Start the current Docker Compose stack
docker compose up -d

Check the status of all services:

Show Docker Compose service status
docker compose ps

View logs in real time:

Follow Docker Compose logs
docker compose logs -f

Stop everything when you are done:

Stop and remove the current Compose stack
docker compose down

Verify volumes were created:

List Docker volumes created by Compose
docker volume ls

With a single file describing your entire infrastructure, you can deploy real applications quickly and reproduce the setup anywhere.

Need a Docker VPS with room for real containers?

Host Docker workloads on Virtarix VPS infrastructure with root access, NVMe storage, snapshots, backups, and capacity to move beyond a first container test.

Step 6: security hardening (do not skip this)

I have seen too many compromised servers because people skipped security. Let’s lock yours down. A critical warning before we start: when you expose container ports using Docker, those ports bypass your firewall rules completely. Many people miss this, and it is a serious security risk. Begin by configuring a UFW firewall with Docker in mind:

Enable UFW and allow SSH HTTP and HTTPS
sudo ufw --force enable
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS

Only open the ports you actually need. Configure Docker to work with UFW:

Disable Docker-managed iptables rules
sudo systemctl stop docker
echo '{
  "iptables": false
}' | sudo tee /etc/docker/daemon.json
sudo systemctl start docker

Next, create a non-root user inside your containers by adding this to your Dockerfile:

Create and use a non-root container user
RUN adduser --disabled-password --gecos '' appuser
USER appuser

Keep Docker updated regularly. Check for updates monthly:

Review and install Docker package upgrades
sudo apt update && sudo apt list --upgradable | grep docker
sudo apt upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Scan images for vulnerabilities with Docker Scout:

Scan the latest Nginx image with Docker Scout
docker scout quickview nginx:latest

Remember: containers inherit the host’s security. If your VPS is compromised, your containers are too, so do not skip these steps.

Step 7: production-ready practices

These are the steps that separate hobby projects from true production deployments. Always set resource limits in your docker-compose.yml to prevent a single container from consuming all resources:

Attempt to limit Nginx memory and CPU in Compose
services:
  web:
    image: nginx
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
restart: unless-stopped

Implement health checks so Docker can automatically restart unhealthy containers:

Attempt to add an Nginx Compose health check
services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", ""]
      interval: 30s
      timeout: 10s
      retries: 3
restart: unless-stopped

Configure proper logging and log rotation to avoid disk bloat. Edit the Docker daemon configuration:

Configure Docker JSON-file log rotation
sudo mkdir -p /etc/docker
echo '{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker

Set up monitoring to keep track of performance and issues. For example, you can use Prometheus:

Attempt to add Prometheus monitoring to Compose
services:
  monitoring:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
restart: unless-stopped

These steps are not optional—they are essential for any application you intend to keep online and stable.

Troubleshooting common Docker issues

Here are the problems seen most often and how to fix them.

“Cannot connect to Docker daemon”

Check if Docker is running:

Show the Docker service status
sudo systemctl status docker

If it is not running, start it:

Start the Docker service
sudo systemctl start docker

Still having problems? Make sure your user is in the Docker group:

Check whether the current user has docker group access
groups | grep docker

“docker: command not found”

This usually means the installation failed. Reinstall Docker properly:

Reinstall Docker Engine and Compose plugins
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Port already in use

Find what is using the port:

Find the process listening on TCP port 80
sudo netstat -tulpn | grep :80

Then kill the process or change the Docker port:

Run Nginx on host port 8080
docker run -p 8080:80 nginx

Out of disk space

Check Docker disk usage:

Show Docker disk usage summary
docker system df

Clean up unused data:

Safety check: Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

Prune unused Docker resources and volumes
docker system prune -a --volumes

Image pull failures

Check internet connectivity:

Ping docker.io to test connectivity
ping docker.io

Try pulling from a different registry:

Pull an Nginx image from Quay
docker pull quay.io/nginx/nginx

We keep these commands handy because even after years of using Docker, unexpected issues can still pop up.

Monitoring and maintenance

Here is a weekly maintenance routine that keeps Docker environments stable and efficient.

Check resource usage:

Review host and Docker resource usage
## Overall system
htop
## Docker specific
docker stats --no-stream
## Disk usage
docker system df

Update containers:

Pull new Compose images and recreate services
## Pull latest images
docker compose pull
## Restart with new images
docker compose up -d

Clean up weekly:

Safety check: Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

Prune Docker resources and inspect container log sizes
## Remove old containers and images
docker system prune
## Check logs are not filling disk
du -sh /var/lib/docker/containers/*/

Monitor key metrics to catch issues early:

  • Memory usage should remain below 80%.
  • Keep at least 5 GB of free disk space.
  • Ensure no containers are constantly restarting.
  • Confirm logs are not growing uncontrollably.

Setting simple alerts for these metrics helps prevent outages before they start.

Real-world deployment example

Here is a production-ready setup that can be adapted to most web applications. Directory structure:

Review an example Docker application directory tree
myapp/
├── docker-compose.yml
├── nginx/
│   └── nginx.conf
├── app/
│   └── Dockerfile
└── .env

Security tip: Never commit .env files to version control. They often contain passwords and API keys.

docker-compose.yml

Define a web app database and Redis Compose stack
services:
  reverse-proxy:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./ssl:/etc/ssl/certs
    depends_on:
      - app
    restart: unless-stopped
  app:
    build: ./app
    environment:
      - DATABASE_URL=mysql://user:${DB_PASSWORD}@database:3306/myapp
    depends_on:
      - database
      - redis
    restart: unless-stopped
  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: myapp
      MYSQL_USER: user
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    restart: unless-stopped
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped
volumes:
  mysql_data:
  redis_data:

Deploy it:

Create Compose passwords and deploy the example stack
## Create .env file with your passwords
echo "DB_PASSWORD=your_secure_db_password" > .env
echo "DB_ROOT_PASSWORD=your_root_password" >> .env
## Launch everything
docker compose up -d
## Verify operation
docker compose ps
curl http://your_server_ip

This structure works for most web applications and can be customized for any tech stack.

Conclusion

You now have everything required to run Docker effectively on a VPS. Starting with at least 4 GB of RAM ensures containers have the resources they need for real workloads.

Use Docker Compose for any project that involves multiple containers. It maintains consistent deployments and simplifies scaling or migration.

Implement monitoring, resource limits, and strong security practices from the beginning. Pay particular attention to firewall settings, as exposed ports can bypass standard rules.

Running Docker on a VPS provides an ideal balance of control, performance, and cost. It avoids vendor lock-in and keeps your environment fully portable.

Begin with a small project, such as containerizing an existing application or deploying a new service. Once you experience the simplicity of containerized deployments, you will see why Docker is a core tool for modern infrastructure.

Ready to host Docker on Virtarix VPS?

Compare VPS sizes for Docker containers, Compose stacks, registries, and production services with root access, NVMe storage, IPv4 + IPv6, snapshots, and backups.

VPS S

For small sites, dev servers and Docker

$ 5 .50 /month
  • 3 cores
  • 6 GB
  • 50 GB NVMe
  • Unlimited
Get It Now
BEST SELLER

VPS M

For growing apps, websites and staging

$ 11 .40 /month
  • 6 cores
  • 16 GB
  • 100 GB NVMe
  • Unlimited
Get It Now
Peter French
About the Author Peter Frenchis the Managing Director at Virtarix, with over 17 years in the tech industry. He has co-founded a cloud storage business, led strategy at a global cloud computing leader, and driven market growth in cybersecurity and data protection.