What Is Docker and Why Use It on VPS?
Docker is a containerization platform that packages applications together with their dependencies (libraries, environment variables, configuration files) into isolated units called containers. Unlike virtual machines, containers share the host OS kernel, making them lightweight and fast to start — typically within seconds.
On a VPS, Docker provides several key advantages:
- Environment consistency — "Works on my machine" problems disappear; the container behaves identically across dev, staging, and production
- Easy deployment — Start complex software (databases, message queues, search engines) with a single command
- Isolation — Applications run in separate containers, preventing dependency conflicts
- Rollback capability — Tag images by version and roll back instantly if an update breaks something
This guide assumes a fresh Ubuntu 22.04 VPS with root or sudo access.
Why install from Docker's official repo? Ubuntu's built-in apt packages Docker as docker.io, which is often several versions behind. Installing from Docker's official repository ensures you get the latest stable Docker Engine and the docker-compose-plugin (v2 syntax: docker compose instead of docker-compose).
Step 1: Prepare the System
Update package lists and install prerequisite packages needed to add Docker's apt repository over HTTPS:
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
Add Docker's Official GPG Key
Docker signs its packages with a GPG key. Add it to Ubuntu's trusted keyring so apt can verify package integrity:
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
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Add Docker's Repository
Add the official Docker apt repository to your sources list:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Step 2: Install Docker Engine and Compose Plugin
Update the package index (now including Docker's repo) and install all required packages:
sudo apt-get update
sudo apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
This installs Docker Engine (docker-ce), the CLI client (docker-ce-cli), the container runtime (containerd.io), and the modern Compose plugin.
Verify the Installation
Run the classic hello-world container to confirm Docker is working correctly:
sudo docker run hello-world
You should see a message: "Hello from Docker! This message shows that your installation appears to be working correctly."
Step 3: Run Docker Without sudo
By default, only root can run Docker commands. Add your user to the docker group so you can run containers without sudo:
sudo usermod -aG docker $USER
Log out and log back in for the group change to take effect. Then verify without sudo:
docker run hello-world
Security note: The docker group grants root-equivalent access on the host because Docker can mount host filesystems. Only add trusted users to this group. Never add the www-data or other service accounts.
Step 4: Run Your First Real Container — Nginx
Let's run a real web server to see Docker in action. The following command runs an Nginx container that maps port 8080 on the host to port 80 inside the container:
docker run -d -p 8080:80 --name my-nginx nginx:alpine
Flags explained:
-d— detached mode (runs in background)-p 8080:80— maps host port 8080 → container port 80--name my-nginx— assigns a readable namenginx:alpine— uses the lightweight Alpine Linux-based Nginx image
Open http://YOUR_VPS_IP:8080 in your browser. You should see the Nginx welcome page.
Understanding the Docker Workflow
Before going further, it helps to understand the three core concepts you will work with daily. An image is a read-only template that contains everything an application needs — code, runtime, libraries, and configuration. A container is a running instance created from an image; you can start, stop, and delete containers without affecting the image they came from. A registry (such as Docker Hub) is where images are stored and shared, similar to how GitHub stores code.
The typical lifecycle looks like this: you pull an image from a registry, run it to create a container, inspect its logs, and eventually stop and rm it. When you need a custom application you write a Dockerfile, then build it into your own image. On a VPS this workflow means you can deploy a new version of your app by pulling a fresh image and recreating the container — a process that takes seconds and is trivially easy to roll back.
| Term | What it is |
|---|---|
Image | Read-only template — the blueprint of your application |
Container | A running, writable instance of an image |
Volume | Persistent storage that survives container deletion |
Network | Private virtual network letting containers talk to each other |
Registry | Storage for images (Docker Hub, GitHub Container Registry) |
Essential Docker Commands
These commands cover 90% of daily Docker usage:
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop my-nginx
# Remove a container
docker rm my-nginx
# List downloaded images
docker images
# Pull an image without running it
docker pull redis:7-alpine
# View container logs (follow mode)
docker logs -f my-nginx
# Execute a command inside a running container
docker exec -it my-nginx sh
Step 5: Deploy a Multi-Container App with Docker Compose
Real applications rarely run as a single container. Docker Compose lets you define and run multi-container apps using a single YAML file. Here's a classic example — WordPress with a MySQL database:
mkdir ~/wordpress && cd ~/wordpress
Create docker-compose.yml:
version: "3.9"
services:
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:latest
restart: always
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
volumes:
- wp_data:/var/www/html
depends_on:
- db
volumes:
db_data:
wp_data:
Start the entire stack with one command:
docker compose up -d
Docker Compose automatically creates a private network between the two containers. WordPress can reach MySQL using the hostname db — the service name in the YAML file. To stop and remove everything:
docker compose down
To also delete the data volumes (WARNING: data will be lost):
docker compose down -v
Step 6: Enable Docker to Start on Boot
Docker's systemd service should already be enabled after installation. Verify with:
sudo systemctl is-enabled docker
# Should output: enabled
sudo systemctl status docker
If not enabled, run:
sudo systemctl enable docker --now
Containers started with --restart always or restart: always in Compose files will automatically restart when the Docker daemon starts.
Step 7: Clean Up Docker Resources
Over time, stopped containers, unused images, and dangling volumes accumulate and consume disk space. Use these commands to reclaim disk space:
# Remove all stopped containers
docker container prune
# Remove unused images
docker image prune
# Remove all unused resources at once (containers, images, networks, build cache)
docker system prune
# Show disk usage by Docker
docker system df
Summary and Next Steps
You have successfully installed Docker Engine and Docker Compose on Ubuntu 22.04, run your first container, and deployed a multi-container WordPress+MySQL stack using Docker Compose.
Next steps to explore:
- Portainer — a web UI to manage Docker containers visually without CLI
- Nginx reverse proxy — route traffic from port 80/443 to multiple containers
- Docker volumes — persistent storage for databases and user uploads
- Private Docker registry — store your own images without relying on Docker Hub
Basic Docker Security on a VPS
Docker is powerful, but a misconfigured host exposed to the internet can become an easy target. Before running production workloads, apply these baseline hardening steps:
- Treat the docker group as root — anyone in the
dockergroup can mount the host filesystem into a container and read or modify any file. Add only trusted administrators, and never add service accounts likewww-data. - Do not expose database ports publicly — when running MySQL, Redis, or PostgreSQL in Compose, let other containers reach them over the internal network instead of publishing the port with
ports:. An exposed database with a weak password is one of the most common breach vectors. - Keep a firewall in front — configure UFW to allow only the ports you actually need (80, 443, SSH). Be aware that Docker manipulates iptables directly and can bypass UFW rules; review your
iptablesrules after publishing ports. - Pull updated images regularly — run
docker pull image:tagto fetch patched versions, then recreate containers from the new image. Avoid leaving images unpatched for months. - Run containers as a non-root user — many images support a
--userflag or aUSERdirective in the Dockerfile, which limits the blast radius if a container is compromised. - Limit container resources — use
--memoryand--cpusflags so a single runaway container cannot exhaust the entire VPS and take down your other services.
Frequently Asked Questions
How is Docker different from a virtual machine?
A virtual machine emulates an entire hardware stack and boots a full guest operating system, which makes it resource-heavy and slow to start. A Docker container shares the host kernel and isolates only the process, filesystem, and network, so it is lightweight, starts in seconds, and lets you run many containers on a single VPS. The trade-off is that all containers must run on the same kernel type (Linux).
How much RAM does my VPS need to run Docker?
For testing and lightweight containers such as Nginx or a static site, 1GB of RAM is enough. To run WordPress with MySQL or several services at once, 2GB or more is recommended. AsiaGB VPS plans start at just 500 THB/month with Ubuntu and several RAM tiers so you can match the plan to your actual workload.
Why install from Docker's repository instead of the default apt package?
Ubuntu's bundled docker.io package is usually several versions behind and lacks the modern docker compose v2 plugin. Installing from Docker's official repository gives you the latest, security-patched Docker Engine and Compose plugin with full support for current features.
How do I make containers start automatically after a reboot?
Add --restart always when running docker run, or set restart: always in your docker-compose.yml. Docker will then start those containers automatically whenever the daemon starts after a reboot. Make sure the Docker service itself is enabled at boot with sudo systemctl enable docker.
Ready to run Docker on a VPS?
AsiaGB VPS starts at just 500 THB/month with full root access, Ubuntu 22.04, and 99% uptime SLA — everything you need to run Docker containers in production.
View VPS Plans →