BLACKBOXHelp Center

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.

generalUpdated 2026-10-09~9 min read
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-plugin

The 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 docker

Check the installation

docker run hello-world
docker compose version

The 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 USERNAME

Log out and log back in as USERNAME. The docker commands then work without sudo.

Security Membership in the docker group 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:

  1. Open the configuration file. On AlmaLinux and CentOS Stream, install the nano editor first:

    dnf install -y nano
    mkdir -p /etc/docker
    nano /etc/docker/daemon.json
  2. Enter 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"
      }
    }
  3. Check the file. The correct result is “configuration OK”. If the file contains a mistake, the docker service won't start, and neither will the containers.

    dockerd --validate --config-file=/etc/docker/daemon.json
  4. Restart Docker. The containers stop briefly; those with restart: unless-stopped or restart: always start 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-recreate

Ports: 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 (-p or ports:), Docker writes its own iptables rules, which are applied before the ufw rules: ufw status doesn't show them, and a deny rule 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:

  1. 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.
  2. 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 example db.
  3. 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.
  4. For advanced filtering, add rules to the DOCKER-USER chain, 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.

  1. Create the project folder:

    mkdir -p /srv/wordpress
    cd /srv/wordpress
  2. Create the .env file 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 .env
    DB_PASSWORD=A_LONG_PASSWORD

    Restrict access to the file to the root user:

    chmod 600 .env
  3. Create the compose.yaml file:

    nano compose.yaml
    services:
      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:
  4. Start the project and check it:

    docker compose up -d
    docker compose ps
    curl -I http://127.0.0.1:8080

    A response that starts with HTTP/1.1 means 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-data and wp-data in 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.sql

Find 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 prune also deletes stopped containers: a project stopped with docker compose stop loses its containers (you recreate them with docker compose up -d). Don't add --volumes and don't run docker volume prune -a unless you know exactly which volumes will be deleted: the volumes of a project stopped with docker compose down count 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_NAME

What you can do:

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 login

How 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.

Didn't find your answer?Write to us and the support team will reply. support@blackbox.md
Open a ticket