Introduction
Virtualization has changed the game in software development. Docker and Docker Compose are must-have tools that let you build, manage and orchestrate containerized applications efficiently. This article walks you step by step through installing Docker and Docker Compose on a Linux system, whether that’s a PC, a virtual machine (VM), a Raspberry Pi or an LXC container (CT) on Proxmox VE.
If you want to take your projects to the next level with lightweight, efficient containers, stick around to the end!
Why Install Docker and Docker Compose?

Before getting into the technical steps, it’s worth understanding why these tools are so popular:
- Fast application deployment: With Docker, you can run entire applications with a single command.
- Centralized service management: Docker Compose makes orchestrating multiple containers simple.
- Isolation and security: Each container runs independently, which minimizes conflicts between applications.
- Portability: Docker containers run in any environment that supports Docker.
- Resource efficiency: Unlike virtual machines, containers share the host operating system kernel, which makes them lighter and faster.
Ready to install these tools? Let’s go!
Prerequisites

Before you start, make sure you have the following:
- Operating system: A Linux distribution such as Ubuntu (20.04/22.04) or Debian (10/11).
- Superuser privileges: You’ll need
sudofor some of the commands. - Internet connection: You’ll be downloading packages.
For this tutorial the installation runs inside an LXC container (CT) on Proxmox VE, but the steps apply to any Linux environment.
Steps to Install Docker and Docker Compose

Step 1: Update the System
First, make sure your system is up to date:
sudo apt update && sudo apt upgrade -yThis ensures every package is on its latest version and avoids possible compatibility errors.
Step 2: Install curl
curl is an essential tool for downloading the Docker installation script. Install it by running:
sudo apt install curl -yCheck that it installed correctly with:
curl --versionYou should see something like this:

Step 3: Install Docker and Docker Compose
Now let’s install Docker and Docker Compose using the official script:
curl -sSL https://get.docker.com/ | shOne note before you run it. That command downloads a script and hands it straight to your shell, so you are trusting the server to return exactly what you expect. It is the method Docker itself offers for getting started quickly and it works well, but their own repository says it plainly: “it is not recommended to depend on this script for deployment to production systems”.
If you would rather see what is about to happen before it happens, download it separately and read it:
curl -fsSL https://get.docker.com -o get-docker.sh
less get-docker.sh
sh get-docker.sh
For a server that will actually run something, the official route is Docker’s package repository, documented at docs.docker.com/engine/install. It takes longer, but it leaves the system updating through apt like any other package.
What does this command do?
- Checks the system:
- Verifies your operating system distribution and version to make sure it’s compatible with Docker.
- Sets up the official repositories:
- Adds the required Docker repositories to your system (APT for Debian/Ubuntu or YUM for CentOS/RHEL).
- Installs Docker:
- Installs the Docker container engine (Docker Engine).
- Installs the Docker command line interface (Docker CLI).
- Includes Docker Compose V2:
- Docker Compose V2 ships inside the Docker CLI binary, so it comes along automatically.
- Enables and starts Docker:
- Configures Docker to start automatically at boot.
- Starts the Docker service right after installation.
- Cleanup and final setup:
- Makes sure the installation is complete and ready to use.
Once the installation finishes, check the installed version:
docker --versionYou should get output like this:
Docker version 27.3.1, build ce12230Then check the Docker Compose installation:
docker compose versionYou should see something like this:
Docker Compose version v2.29.7Step 4: Let your user run Docker
Docker talks through a socket owned by root. Straight after installing, any command from your regular user answers with this:
permission denied while trying to connect to the Docker daemon socket
at unix:///var/run/docker.sock
If you are working as root (inside a Proxmox LXC container, for instance) you will never see this error and can skip the step. Otherwise, add your user to the docker group:
sudo usermod -aG docker $USER
And here is the part that costs people half an hour: the change does not apply to your current session. You add the group, run the command again, and it fails in exactly the same way. Log out and back in, or force the refresh without leaving:
newgrp docker
groups
docker should appear in the list that groups returns.
It is worth understanding what you just granted: being in the docker group is equivalent to having root on that machine. From there you can start a container that mounts the host’s entire disk and read or write it without restriction. This is not a harmless convenience shortcut. On your own server it is perfectly reasonable; on a machine shared with other people, leave the group alone and type sudo docker instead.
Step 5: Test the Installation
To make sure Docker is working properly, run a test container:
docker run hello-worldIf everything is set up correctly, you’ll see a confirmation message telling you Docker is working.

Your first docker-compose.yml
You have installed both pieces, but so far you have only used one. Docker Compose is what turns a long, unrepeatable command into a file you can save, version, read six months from now and bring back up on another machine without remembering a thing.
The difference is clearest side by side. This is starting Uptime Kuma (a service monitor) by hand:
docker run -d --restart=always -p 3001:3001 \
-v uptime-kuma:/app/data --name uptime-kuma \
louislam/uptime-kuma:1
It works. But three months from now, when you want to change the port or move the service to another server, that command will be long gone from your shell history. This is exactly the same thing, in a file called docker-compose.yml:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: always
ports:
- "3001:3001"
volumes:
- uptime-kuma:/app/data
volumes:
uptime-kuma:
Line by line, because each one earns its place:
| Key | What it does |
|---|---|
services: | The list of containers. There is one here, but there could be ten and they would all start together. |
image: | Which image to use. The :1 at the end is not decoration: it pins the major version. Without it you land on :latest, and the day the project jumps to version 2 you go with it without noticing. |
restart: always | Brings the container back after a reboot or a crash. |
ports: | "3001:3001" reads as host_port : container_port, in that order. Swapping them is the most common mistake there is. |
volumes: | Where the data lives. This is the line that prevents disaster: without it, deleting the container deletes your entire configuration and history. |
Save it in its own folder and bring it up:
mkdir -p ~/services/uptime-kuma
cd ~/services/uptime-kuma
nano docker-compose.yml # paste the content from above
docker compose up -d
Every Compose command runs from the folder holding the file. These four cover 95% of what you will ever need:
docker compose ps # what is running
docker compose logs -f # follow the logs live
docker compose down # stop and remove (volumes survive)
docker compose pull && docker compose up -d # update
That last line is the one that wins people over: updating any service is two commands, always the same two, with no need to remember which flags you started it with. And if something breaks, you change one line in the file and bring it back up.
A warning about older tutorials. You will find countless examples that open with version: "3.8" on the first line. That is no longer needed: the Compose specification marks the field as obsolete and current versions ignore it, printing a warning. If a tutorial insists on it, it was written for Compose v1 and probably has more than that out of date.
You now have what the title of this guide promises. From here on, almost every service you will see on this blog is deployed with a file just like that one: only the image name and the ports change.
Next Steps
Done! Docker and Docker Compose are now set up on your system. From here you can start to:
- Learn the basic commands: Explore commands like
docker ps,docker stopanddocker rm. - Build multi-container applications: Learn how to work with
docker-compose.ymlfiles. - Deploy real projects: Run applications like Nginx, Redis, or databases such as MySQL and PostgreSQL.
Further Reading
To go deeper into Docker and Docker Compose, take a look at these sources:
Final Thoughts

Docker and Docker Compose are not just handy tools, they are powerful allies for developers and DevOps teams. With this basic installation in place, you’re already on your way to streamlining your workflows and improving how you deploy applications.
If you found this article useful, share it with your colleagues and friends!
Still have questions? Leave them in the comments and I’ll be happy to help.
Next steps: what to deploy with Docker
With Docker and Docker Compose installed, you have the foundation to deploy just about any self-hosted service. Here are a few projects you can follow on this blog:
- Install Nginx Proxy Manager: manage your reverse proxies and SSL certificates from a web interface
- Install Uptime Kuma: monitor your services’ availability with real-time alerts
- Self-host DeepSeek AI: run your own AI model locally
- Automate with ActivePieces: build automation workflows without relying on paid services
- Install OpenClaw: your personal AI assistant on WhatsApp, Telegram and more
To expose these services securely without opening ports on your router, check out how to create a free Cloudflare tunnel.
Essential Docker commands
Some commands you’ll use constantly:
# List running containers
docker ps
# List all containers (including stopped ones)
docker ps -a
# View a container's logs
docker logs container-name
# Stop a container
docker stop container-name
# Remove a stopped container
docker rm container-name
# Update a service with docker-compose
cd /path/to/project
docker compose pull
docker compose up -d
