Install Docker and Docker Compose on Ubuntu
Docker runs applications with all their dependencies in containers, and Docker Compose starts several containers (for example web, database and cache) from one compose.yaml file. In this tutorial you install Docker Engine and the Compose plugin from Docker's official repository (Ubuntu's own package is older), run a sample service, and learn to publish ports without leaving them open by accident.
Prerequisites
- A cloud server or VPS with Ubuntu 24.04 or 22.04
- A user with sudo (first SSH login and hardening)
- At least 1 GB of RAM for light work; for several services a bigger plan or swap
download.docker.com and Docker Hub restrict access from Iranian IPs, and you may get 403 Forbidden. On servers in Iranian data centers, use a local mirror of the repository and registry, or create the server in a data center outside Iran.
1. Remove old or conflicting packages
If you installed Docker from Ubuntu's repository before, remove it (packages that are not installed only print a message):
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove -y $pkg
done
Existing images and volumes in /var/lib/docker are kept.
2. Add Docker's official repository
sudo apt-get update
sudo apt-get install -y 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
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
These commands detect the release name themselves (noble for 24.04, jammy for 22.04).
3. Install Docker Engine and Compose
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The Docker service is running after install and starts on boot. Check:
sudo systemctl is-enabled docker
sudo docker run --rm hello-world
docker compose version
Compose v2 is a plugin and runs as docker compose (with a space), not docker-compose.
4. Run Docker without sudo (optional)
sudo usermod -aG docker $USER
Log out of SSH and back in, then try docker ps without sudo.
Members of the docker group effectively have root access to the server. Only add users you would give root.
5. Your first Docker Compose project
mkdir -p ~/hello && cd ~/hello
cat > compose.yaml <<'EOF'
services:
web:
image: nginx:alpine
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
EOF
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080
HTTP/1.1 200 OK means the container works. See logs with docker compose logs -f and stop the service with docker compose down.
restart: unless-stopped brings the container back after a server reboot.
6. Docker and the firewall
Docker creates its own iptables rules for published ports, and those rules are evaluated before ufw. So ports: - "8080:80" opens port 8080 on every IP of the server, even if ufw blocks it.
Safe options:
- Publish ports only on
127.0.0.1(as in the example above) and put a reverse proxy such as Nginx in front. - For server-to-server traffic, publish the port on the private network IP, for example
"10.0.1.10:5432:5432". - Use the Arianet cloud firewall; it runs outside the server and Docker cannot bypass it.
7. Limit log size (recommended)
Container logs grow without limit by default and can fill the disk. The local driver keeps logs compressed and rotated:
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"log-driver": "local"
}
EOF
sudo systemctl restart docker
This only affects containers created from now on; recreate your projects with docker compose up -d --force-recreate.
Install at server creation
In Arianet Cloud you can install Docker automatically when creating a server, in the "Startup script (cloud-init)" step (or --user-data-file in the CLI and user_data in Terraform). This example uses Docker's convenience script, which is fine for testing; for production prefer the repository method above:
#cloud-config
runcmd:
- curl -fsSL https://get.docker.com | sh
Common errors
| Error | Fix |
|---|---|
permission denied while trying to connect to the Docker daemon socket | Use sudo, or add the user to the docker group and log in to SSH again. |
403 Forbidden on apt-get update or docker pull | Access restriction from Iran; see "Servers in Iran" above. |
toomanyrequests: You have reached your pull rate limit | Docker Hub's anonymous pull limit. Run docker login or use another registry. |
bind: address already in use | Another service has the port: sudo ss -tlnp | grep 8080. |
| Disk full | docker system df shows usage; docker system prune removes unused images and containers. For a lot of data, attach a separate volume. |
Next steps
- Nginx and a free Let's Encrypt certificate in front of your containers
- Attach and mount an Arianet cloud volume for container data
- Create servers with Terraform and the Arianet CLI
Create your server
Create an Arianet cloud server to run your containers; it is billed hourly and you delete it whenever you no longer need it. In the Arianet panel, go to Cloud server / Cloud Computing → New server, pick Ubuntu 24.04 and put the cloud-init example above in the startup script.