Docker lets you run applications in containers: isolated environments that include everything the application needs. No conflicting library versions, no dependency chaos, no "it works on my machine" problems. Websites, databases, bots, and game servers can each run in their own container with their own dependencies, completely independent of each other.
This guide covers the essentials: installing Docker on Ubuntu, running your first container, and using Docker Compose for multi-container setups. By the end, you will be able to deploy applications on your VPS with confidence.
Installing Docker on Ubuntu
Always use the official Docker repository, not the Ubuntu package repository (which ships an outdated version):
curl -fsSL https://get.docker.com | sh
This script detects your distribution and installs the latest Docker Engine and Docker Compose plugin. Verify the installation:
docker --version docker compose version
Both commands should return version numbers. If docker compose is not recognized, the plugin did not install. On Ubuntu 24.04 with the official script, this rarely happens.
Running your first container
Start an Nginx web server in a container:
docker run -d --name webserver -p 80:80 nginx
Open your VPS IP address in a browser. You should see the Nginx welcome page. That single command downloaded the Nginx image, created a container, and started it.
Breaking down the flags:
-d: run in the background (detached mode).--name webserver: give the container a recognizable name instead of a random string.-p 80:80: map port 80 on the host to port 80 in the container.
Docker Compose for multi-container setups
Real applications usually need more than one service. A website needs a web server and a database. A Discord bot needs the bot process and possibly a database. Docker Compose manages multiple containers as a single unit.
Create a file called docker-compose.yml:
services:
web:
image: nginx
ports:
- "80:80"
volumes:
- ./site:/usr/share/nginx/html
db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: strongpassword
MYSQL_DATABASE: mydb
volumes:
- dbdata:/var/lib/mysql
volumes:
dbdata:
Start everything with docker compose up -d. Both containers start and can communicate with each other using their service names as hostnames (the web container can reach the database at db:3306).
Practical examples
- WordPress: a
docker-compose.ymlwith WordPress and MariaDB services. Five minutes from empty VPS to a working WordPress site. - Discord bot: write a Dockerfile for your Python or Node.js bot, build the image, and run it with
restart: unless-stoppedso it comes back after crashes and reboots. - Database: MariaDB, PostgreSQL, or MongoDB in a container with a volume for persistent data. Isolated from the host, easy to back up, and simple to upgrade.
- Reverse proxy: Nginx or Traefik as a container that routes traffic to other containers based on domain name. Run multiple websites on one VPS, each in its own container.
Essential commands
| Command | What it does |
|---|---|
docker ps | List running containers |
docker ps -a | List all containers, including stopped ones |
docker logs webserver | View logs for a container |
docker stop webserver | Stop a container |
docker rm webserver | Remove a stopped container |
docker exec -it webserver bash | Open a shell inside a running container |
docker compose up -d | Start all services defined in docker-compose.yml |
docker compose down | Stop and remove all services |
docker compose logs -f | Follow logs from all services in real time |
Volumes: keeping your data
Containers are disposable. When you remove a container, any data stored inside it is gone. Volumes solve this: they store data outside the container, on the host filesystem. Always use a volume for databases, uploaded files, and anything you cannot afford to lose.
In a Compose file, volumes are defined at the bottom and referenced in the service. In the example above, dbdata keeps the MariaDB data safe even if the container is rebuilt.
Updating containers
To update a containerized application:
docker compose pull docker compose up -d
This pulls the latest image versions and recreates any container whose image has changed. Your data stays intact because it lives in volumes, not in the container itself.
Security basics
- Do not run containers as root inside the container when you can avoid it. Many images support running as a non-root user.
- Keep Docker updated. The official install script gives you the latest version; run it periodically or use
apt update && apt upgrade. - Do not expose database ports to the internet. If your database is only used by another container, skip the
ports:section. Docker's internal network handles the connection.
Is Docker hard to learn?
The basics (starting containers, stopping them, writing Compose files) can be learned in an afternoon. Advanced topics like custom networking, multi-stage builds, and orchestration with Kubernetes take more time, but most VPS users never need them.
Can I combine Docker with Nginx running on the host?
Yes. A common setup: Nginx on the host acts as a reverse proxy, forwarding traffic to Docker containers. Each container listens on an internal port; Nginx routes requests based on the domain name. This gives you SSL termination at the Nginx level and clean separation between applications.
Does Docker use extra memory?
Minimal. Docker shares the host kernel; a container adds roughly 10 to 50 MB of overhead, far less than a virtual machine. The application inside the container uses the same amount of memory as it would without Docker. The real benefit is isolation, not resource savings.
Run Docker on a KVM VPS from HostValues with full root access and install anything you need.