Installing Docker and Docker Compose
How to install Docker from the official repository, run applications with Compose, and what you need to know about ports, logs, disk space and memory.
On this page
Docker runs applications in isolated containers, and Docker Compose starts several containers from a single file. BLACKBOX VPS servers don't come with Docker preinstalled: you install it yourself after you connect to the server over SSH as root (Connecting to your server over SSH (Windows, macOS, Linux)). The commands follow the official Docker documentation.
Available to: anyone with root SSH access to a BLACKBOX server running Ubuntu, Debian, AlmaLinux 9 or CentOS Stream 9. The app.blackbox.md panel does not install or manage containers on servers ordered on blackbox.md.
Before you start
- For each distribution, Docker publishes a list of supported versions. Ubuntu 20.04 and Debian 11 are no longer on that list. For a new server with Docker, choose Ubuntu 24.04 LTS, Ubuntu 22.04 LTS or Debian 12: Available operating systems.
- If you have a “VPS hosting” plan with a control panel, ask support before you install Docker: the panel's web server usually already uses ports 80 and 443. See “VPS hosting” plans with a control panel.
Installing Docker Engine and the Compose plugin
The docker-compose-plugin package provides the docker compose command (with a space). The old docker-compose command (with a hyphen) is no longer needed.
Ubuntu and Debian
Run the commands in the same session. . /etc/os-release reads the distribution name (ubuntu or debian) and the version codename (for example noble or bookworm), so the block is the same on both systems.
apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
curl -fsSL https://download.docker.com/linux/$ID/gpg -o /etc/apt/keyrings/docker.asc
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/$ID $VERSION_CODENAME stable" > /etc/apt/sources.list.d/docker.list
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginThe docker service starts on its own after installation and every time the server restarts.
AlmaLinux and CentOS Stream
The Docker documentation has no page for AlmaLinux. On AlmaLinux 9, the CentOS repository below works.
dnf -y install dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now dockerCheck the installation
docker run hello-world
docker compose versionThe first command downloads a test image and prints “Hello from Docker!”. The second prints “Docker Compose version”, followed by the installed version number.
A non-root user (optional)
If you work as a regular user (created as described in Securing a new server: checklist), add that user to the docker group:
usermod -aG docker USERNAMELog out and log back in as USERNAME. The docker commands then work without sudo.
Security Membership in the
dockergroup is equivalent to root access: a group member can start a container with the server's entire disk mounted and change any file. Add to the group only users you would also give root access to.
Limit container logs
By default, Docker keeps everything a container outputs, in *-json.log files in /var/lib/docker/containers/, with no size limit. A container that writes a lot can fill the disk. Set up log rotation right after installation:
Open the configuration file. On AlmaLinux and CentOS Stream, install the
nanoeditor first:dnf install -y nanomkdir -p /etc/docker nano /etc/docker/daemon.jsonEnter the content below. If the file already has settings, add the two keys to the existing object instead of adding a second
{ }block.{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }Check the file. The correct result is “configuration OK”. If the file contains a mistake, the
dockerservice won't start, and neither will the containers.dockerd --validate --config-file=/etc/docker/daemon.jsonRestart Docker. The containers stop briefly; those with
restart: unless-stoppedorrestart: alwaysstart again on their own.systemctl restart docker
Each container then keeps at most 3 files of 10 MB each. The setting applies only to containers created after the change: recreate a Compose project's containers from the project folder, and delete and recreate the ones you started with docker run.
docker compose up -d --force-recreatePorts: what reaches the internet
- The network filter managed by BLACKBOX lets through only SSH (22), HTTP (80), HTTPS (443) and ping by default. A container published on 8080 or 3000 is not reachable from the internet. For another port, write to support: How to contact BLACKBOX support.
- Docker bypasses the system firewall. For published ports (
-porports:), Docker writes its own iptables rules, which are applied before the ufw rules:ufw statusdoesn't show them, and adenyrule doesn't close them. Don't rely on firewalld to hide a port published by Docker either.
Warning A container published on 80, on 443 or on a port opened at your request is reachable from the internet regardless of ufw or firewalld. Don't publish databases or admin panels on all of the server's addresses.
How to publish correctly:
- Publish the application only on
127.0.0.1("127.0.0.1:8080:80"instead of"8080:80") and expose it through nginx on 80/443, with HTTPS: Node.js, Python and other services: nginx as a reverse proxy. For AlmaLinux and CentOS Stream, that guide also shows the SELinux setting you need. - Containers that only talk to each other (for example, the database) don't need
ports:: within the same Compose project, they find each other by service name, for exampledb. - A proxy container (nginx, Caddy, Traefik) can be published directly on 80 and 443. In that case, don't also run nginx or Apache on the server itself, or startup fails with the error
address already in use. - For advanced filtering, add rules to the
DOCKER-USERchain, following the Docker documentation on iptables. Rules you add manually are lost on reboot unless you save them.
Your first Docker Compose project
The example starts WordPress with a MariaDB database. The site listens only on 127.0.0.1:8080, and the data is kept in named volumes. You then publish it on your domain through nginx, as in step 1 above.
Create the project folder:
mkdir -p /srv/wordpress cd /srv/wordpressCreate the
.envfile with the database password. Compose reads it automatically. Use only unaccented Latin letters and digits in the password: Compose treats the$character in this file as a variable.nano .envDB_PASSWORD=A_LONG_PASSWORDRestrict access to the file to the
rootuser:chmod 600 .envCreate the
compose.yamlfile:nano compose.yamlservices: db: image: mariadb:11 restart: unless-stopped environment: MARIADB_DATABASE: wordpress MARIADB_USER: wordpress MARIADB_PASSWORD: ${DB_PASSWORD} MARIADB_RANDOM_ROOT_PASSWORD: "1" volumes: - db-data:/var/lib/mysql wordpress: image: wordpress:latest restart: unless-stopped depends_on: - db environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} WORDPRESS_DB_NAME: wordpress ports: - "127.0.0.1:8080:80" volumes: - wp-data:/var/www/html volumes: db-data: wp-data:Start the project and check it:
docker compose up -d docker compose ps curl -I http://127.0.0.1:8080A response that starts with
HTTP/1.1means the site is running. If you get an error right after startup, wait a few seconds: on the first start, the database is being initialized.
With restart: unless-stopped, the containers start on their own after the server restarts. Run Compose commands from the project folder:
| Command | What it does |
|---|---|
docker compose up -d |
creates and starts the containers in the background |
docker compose logs -f |
follows the logs; exit with Ctrl+C |
docker compose pull, then docker compose up -d |
downloads the new images and recreates the containers that use them |
docker compose down |
stops and deletes the containers; the data volumes remain |
docker compose down -v |
also deletes the volumes, that is, all of the project's data |
Data: volumes and backups
Containers are disposable: when you update, Compose deletes and recreates them, and anything the application wrote outside a volume is lost. Keep your data:
- in named volumes, such as
db-dataandwp-datain the example. Docker stores them in/var/lib/docker/volumes/, and Compose adds the project (folder) name in front:wordpress_db-data; - or in server folders mounted into the container (bind mounts), for example
./uploads:/app/uploads, which you can easily copy with rsync.
Back up the volumes and the compose.yaml and .env files, not the containers: images and containers can be recreated at any time. For the database, make a dump instead of copying the files of a running database:
mkdir -p /root/backup
cd /srv/wordpress
docker compose exec -T db sh -c 'mariadb-dump -u wordpress -p"$MARIADB_PASSWORD" wordpress' > /root/backup/wordpress.sqlFind the exact name of the volume, then archive it with a temporary container:
docker volume ls
docker run --rm -v wordpress_wp-data:/data:ro -v /root/backup:/backup alpine tar czf /backup/wp-data.tar.gz -C /data .BLACKBOX does not make automatic backups of your server. Move the copies to another server or to external storage: Backup strategy for your server.
Freeing up space used by Docker
See how much space images, containers, volumes and the build cache take up, and how much can be freed (the RECLAIMABLE column):
docker system df| Command | What it deletes |
|---|---|
docker image prune |
untagged images (<none>) left over after updates |
docker image prune -a |
all images that no container uses |
docker builder prune |
the build cache |
docker system prune |
stopped containers, unused networks, untagged images and the build cache |
docker system prune -a |
in addition, all unused images |
Each command asks for confirmation before deleting anything. If the disk is already full, including because of old container logs, see The server's disk is full.
Risk
docker system prunealso deletes stopped containers: a project stopped withdocker compose stoploses its containers (you recreate them withdocker compose up -d). Don't add--volumesand don't rundocker volume prune -aunless you know exactly which volumes will be deleted: the volumes of a project stopped withdocker compose downcount as unused, and their data is lost permanently.
Memory on small plans
Every container uses memory. A database, an application and a proxy together can exceed the memory of the Basic XS (1 GB), Basic S, CPU S and Standard S (2 GB) plans: VPS plans: how to choose. Check memory use, and whether a container was stopped for lack of memory (a true result confirms it):
free -h
docker stats --no-stream
docker inspect --format '{{.State.OOMKilled}}' CONTAINER_NAMEWhat you can do:
- add swap space: Adding swap space;
- limit a container's memory with the line
mem_limit: 512munder that service incompose.yaml; - move to a larger plan: Server upgrade: moving to a larger plan.
Images take up space too: on 20–30 GB disks, check docker system df regularly. In the CPU and High-Frequency Compute families, the disk stays the same even after an upgrade.
Frequently asked questions
Why don't I see my containers in the app.blackbox.md panel?
The panel does not show or manage containers on servers ordered on blackbox.md. You manage them over SSH, with docker and docker compose.
The container is running, but the application doesn't open from the internet. Why?
Most often, the application is published either on a port other than 80 or 443, which the network filter blocks, or only on 127.0.0.1. Publish it through nginx on 80/443, or ask support to open the port. Other causes: The site does not open.
I get the toomanyrequests error when downloading an image. What do I do?
Docker Hub limits downloads without an account. Create a free Docker Hub account, sign in on the server with the command below, and try the download again.
docker loginHow do I update Docker?
Docker packages are updated along with the system, with apt upgrade or dnf upgrade; on Ubuntu and Debian, automatic security updates don't include them. During the update, the containers stop briefly. Details: System updates and automatic security updates.