Docker and Docker Compose
Docker runs applications in isolated containers — no manual dependency installs, no version conflicts on the host. This article covers installing Docker Engine and the Docker Compose plugin on an mHost VPS, running your first container, and walking through a docker-compose.yml example with two services.
Docker is containers, not virtualization
Docker containerizes applications at the OS level: every container on a server shares the same host Linux kernel and is isolated from the others by ordinary kernel mechanisms (namespaces, cgroups) — no hypervisor, no guest operating systems inside. That's exactly why containers are lightweight and start fast: a container isn't a "tiny virtual machine," it's a regular process isolated on the same kernel.
What you'll need
- An mHost VPS running Ubuntu 22.04/24.04, Debian 11/12, or AlmaLinux/Rocky Linux 8/9.
- SSH access as
rootor a user withsudorights. - The server's public IP — you'll need it to check the container from a browser.
Step 1. Install Docker Engine and the Docker Compose plugin
There's no ready-made VMmanager 6 recipe for Docker (unlike, say, LEMP or Django — see the full list in "Ready-made VMmanager 6 recipes") — you install it manually, using either method below.
We'll install docker-ce — Docker's own official build — rather than docker.io from the distro's repository (which is usually an older version). The Compose plugin (docker-compose-plugin) is installed with the same package manager and adds the docker compose command (two words, no hyphen) — the modern replacement for the old standalone docker-compose binary (with a hyphen), which is no longer developed.
Official repository: Ubuntu and Debian
If Docker was already installed on this server from the distro's own repository, remove the old packages first:
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc | cut -f1)Set up Docker's official repository and install the packages:
sudo apt update
sudo apt 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
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginOfficial repository: AlmaLinux and Rocky Linux
Both distros are RHEL-compatible, so Docker's official RHEL repository works:
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now dockerdnf may ask you to confirm the repository's GPG key — check the fingerprint it shows against the one listed on the official docs.docker.com/engine/install/rhel/ page before accepting.
Quick method: the get.docker.com script
For testing or development, you can install Docker with a single command — a universal script that detects your distro automatically:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.shStep 2. Verify the installation
sudo systemctl status docker
sudo docker run hello-worldIf everything is working, docker run hello-world reports that the Docker client successfully contacted the daemon, pulled the test image, and ran it.
Running without sudo (optional)
By default, only root (or a user going through sudo) can manage Docker — the daemon runs as root, and the /var/run/docker.sock socket is only accessible to root and the docker group. To run docker commands as a regular user without sudo:
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world(if the docker group already exists — which is usually the case after installing docker-ce via apt/dnf — groupadd will just say the group already exists; that's not an error)
For consistency, the rest of this article shows docker/docker compose commands with sudo — if you set up passwordless access as above, you can drop it.
Step 3. Run your first container
Start Nginx in the background with port 80 published:
sudo docker run -d --name web --restart unless-stopped -p 80:80 nginx:alpine-d— run in the background (detached).--name web— name the container so you don't have to remember a random ID.--restart unless-stopped— restart the container automatically, including after a server reboot, until you stop it yourself.-p 80:80— publish host port 80 to port 80 inside the container.
Check that the container is running, then open the server's IP in a browser:
sudo docker ps
curl -I http://localhostOnce you're done with this container, stop and remove it — otherwise it will hold port 80, which you'll need in the next step:
sudo docker rm -f webStep 4. docker-compose.yml in practice
Compose describes several containers in a single file, instead of typing out docker run with a dozen flags for each service by hand.
Create a project directory and a file for the static page:
mkdir -p ~/myapp/html && cd ~/myapp
echo "<h1>Hello from Docker Compose</h1>" > html/index.htmlCreate the docker-compose.yml file:
nano docker-compose.ymlservices:
web:
image: nginx:alpine
container_name: web
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
depends_on:
- db
db:
image: postgres:16
container_name: db
restart: unless-stopped
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD: change_me
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:What's going on here:
services— the project's containers:web(Nginx) anddb(PostgreSQL).image— which image to use;restart— the restart policy, same as indocker run.ports— host:container port publishing, same as the-pflag.volumes— forweb, this is a bind mount of the./htmldirectory from the host into the container, read-only (:ro); fordb, it's the named volumedb_data, where PostgreSQL keeps its data independent of the container's lifecycle.environment— environment variables inside the container.depends_on— Compose startsdbbeforeweb.
Start the project:
sudo docker compose up -d
sudo docker compose psView the logs and stop the project:
sudo docker compose logs -f
sudo docker compose downCommon commands
Docker
docker pull <image>— download an image without running it.docker images— list downloaded images.docker build -t <name> .— build an image from a Dockerfile in the current directory.docker ps— list running containers.docker ps -a— list all containers, including stopped ones.docker logs -f <container>— follow a container's logs in real time.docker exec -it <container> sh— open a shell inside a running container (on Debian/Ubuntu-based images,bashis usually available instead ofsh).docker stop <container>/docker start <container>/docker restart <container>— stop / start / restart.docker rm <container>— remove a stopped container (-fforces it, even if running).docker rmi <image>— remove an image.docker system prune— remove unused containers, images, and networks, freeing up disk space.
Docker Compose
docker compose up -d— create and start all the project's services in the background.docker compose ps— status of the current project's services.docker compose logs -f— follow all the project's service logs in real time.docker compose stop/docker compose restart— stop / restart services without removing the containers.docker compose down— stop and remove the project's containers and network (volumes stay).docker compose exec <service> sh— shell inside a specific service's container.docker compose pull— update the services' images to their current versions.
What's next
If you're not set on Docker specifically and just need a ready-made web stack (LAMP/LEMP) or a specific app (Zabbix, Tomcat, and others) — there may already be a one-click recipe for it in VMmanager 6: see "Ready-made VMmanager 6 recipes". If your containers are exposed to the outside, don't forget the firewall: "Setting up the firewall: ufw".