How to Host Docker Containers on a VPS
This guide walks you through deploying Docker containers on a virtual private server (VPS), from initial setup to keeping containers running smoothly. You’ll learn how to install Docker, run your first container, manage ports and volumes, and use Docker Compose for multi-container apps—everything you need to move from a local dev environment to a live server.
What You’ll Need
- A VPS with a public IP address (any provider: DigitalOcean, Linode, AWS EC2, Hetzner, etc.) running Ubuntu 22.04 or 24.04 LTS (other distros work, but commands may differ).
- SSH access to the server (root or a user with sudo privileges).
- A domain name pointing to your VPS IP if you plan to serve web traffic (optional but recommended).
- Basic familiarity with the Linux command line.
Step-by-Step
- Connect to your VPS via SSH. Open a terminal and run:
ssh user@your_server_ip. If you’re using a key pair, make sure the private key is available. - Update the system. Run
sudo apt update && sudo apt upgrade -yto get the latest package lists and security patches. - Install Docker using the official repository. Run the following commands one by one:
sudo apt install -y 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 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] 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 - Install Docker Engine and related tools. Run
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin. This installs the daemon, CLI, and Compose plugin. - Verify Docker is running. Execute
sudo systemctl status docker(should show “active (running)”) or simplysudo docker --version. - Add your user to the docker group. Run
sudo usermod -aG docker $USERto avoid typingsudofor every Docker command. Log out and back in for the change to take effect. - Test with a simple container. Run
docker run hello-world. If you see the “Hello from Docker!” message, everything works. - Run your first real container. For example, to run an Nginx web server:
docker run -d --name web -p 80:80 nginx:alpine. The-dflag runs it detached, and-p 80:80maps the container’s port 80 to the host’s port 80. - Check that it’s accessible. Open your browser and visit
http://your_server_ip. You should see the Nginx welcome page. - Manage containers. Use
docker psto list running containers,docker logs webto view logs,docker stop webto stop it, anddocker rm webto remove it. - Persist data with volumes. When a container is removed, its data is lost. To keep data, create a volume:
docker volume create mydata, then mount it:docker run -d --name db -v mydata:/var/lib/postgresql/data postgres:16. This way the database survives container recreation. - Use Docker Compose for multi-container apps. Create a
docker-compose.ymlfile in a directory. For a simple app with Nginx and Redis:services: web: image: nginx:alpine ports: - "80:80" redis: image: redis:alpineThen run
docker compose up -dto start everything. This is far easier than managing individualdocker runcommands. - Set up a firewall. If you’re using UFW, allow SSH and HTTP/HTTPS:
sudo ufw allow OpenSSH,sudo ufw allow 80,sudo ufw allow 443, then enable withsudo ufw enable. Be careful not to lock yourself out—always allow SSH first. - Enable Docker to start on boot. Run
sudo systemctl enable dockerto ensure the daemon starts automatically after a reboot. - Keep Docker updated. Regularly run
sudo apt update && sudo apt upgrade -yto get security patches for Docker itself.
Common Mistakes
- Forgetting to map ports. If you can’t reach your container, check that you used
-p host_port:container_port. The container’s internal port is not automatically exposed. - Running containers as root. Many official images support a non-root user. Use
--useror setUSERin your Dockerfile to reduce security risks. - Not using volumes for databases. If you don’t mount a volume, deleting the container deletes your data.
- Binding to port 80 without a firewall. Your container might be exposed to the internet even if you think it’s private. Always configure a firewall.
- Using
latesttags in production. Tags likenginx:latestchange over time and can break your setup. Pin to a specific version likenginx:1.27.0.
Tips
- Use Docker Compose from the start. Even for a single container, having a
docker-compose.ymlmakes it easy to recreate and document your setup. - Set restart policies. Add
restart: unless-stoppedto your compose file or--restart unless-stoppedtodocker runso containers come back after a reboot or crash. - Monitor resource usage. Use
docker statsto see CPU and memory usage per container. This helps you spot leaks or misconfigurations. - Use named volumes over bind mounts. Named volumes are easier to back up and move between hosts.
- Keep images small. Prefer Alpine-based images when possible—they reduce disk usage and attack surface.
- Set up a reverse proxy. For web apps, use Nginx or Traefik as a reverse proxy to handle SSL and route traffic to multiple containers on different ports.
Troubleshooting
- Docker daemon not starting. Check
sudo systemctl status dockerfor errors. Common causes: missing kernel modules or a full disk. Rundf -hto check disk space andsudo systemctl restart docker. - Port already in use. If
docker run -p 80:80fails with “address already in use”, another process (or container) is using port 80. Usesudo lsof -i :80to find it and stop it or change the host port. - Container exits immediately. Run
docker logs container_nameto see the error. Often it’s a missing configuration or the process fails because it has no terminal. Use-itfor interactive processes or check the image’s documentation. - Cannot connect to the internet from inside a container. Check your host’s network and DNS. Try
docker run --rm alpine ping 8.8.8.8; if that works butping google.comfails, your DNS is broken—edit/etc/docker/daemon.jsonto set"dns": ["8.8.8.8"]and restart Docker. - Permission denied when running Docker commands. You haven’t added your user to the docker group or haven’t logged out/in after doing so. Use
sudoas a temporary workaround.
Frequently Asked Questions
Do I need a VPS with a lot of RAM?
It depends on your containers. A basic Nginx container uses about 50 MB. A small Node.js app might use 200 MB. For most small projects, 1 GB RAM is enough. For databases or multiple services, go with 2 GB or more.
Can I host multiple websites on one VPS using Docker?
Yes. Run each site in its own container, map them to different host ports (e.g., 8080, 8081), and use a reverse proxy like Nginx or Traefik to route domain names to the correct container.
How do I update a running container to a newer image?
Pull the new image (docker pull image:tag), stop and remove the old container (docker stop name && docker rm name), then run a new container with the same options. With Compose, just run docker compose pull and docker compose up -d.
Is it safe to use Docker on a public-facing VPS?
Yes, if you follow security best practices: keep Docker updated, run containers as non-root, use a firewall, and avoid exposing the Docker daemon socket (port 2375) to the internet.
What’s the difference between an image and a container?
An image is a read-only template with the application and its dependencies. A container is a running instance of that image. You can have many containers from the same image.
How do I back up data from a Docker volume?
Use a temporary container to archive the volume: docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar czf /backup/mydata.tar.gz -C /data .. This creates a tar file in your current directory.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.