Daily-It

개발, AI, 인프라, 자동화와 일상 IT 제품 후기를 직접 써보며 정리하는 기술 블로그입니다.

Docker Concepts and Ubuntu Installation Guide: Containers, Compose, and Common Pitfalls

Summary

Docker packages an application and the environment it needs into a container, so the same service can run more consistently on a laptop, a CI runner, and a server. The usual reason people search for Docker is simple: “it works on my machine, but not on the server.” Docker does not remove every deployment problem, but it makes the runtime, libraries, ports, and startup commands easier to describe and repeat.

This guide keeps the scope practical: what Docker images and containers are, how Docker differs from a virtual machine, how to install Docker Engine on Ubuntu, when Docker Desktop is used on Windows/macOS, how to read a basic Dockerfile and Compose file, and which mistakes tend to cause real confusion when a beginner moves from the first hello-world command to a small development environment.

Checked against the Korean source article, Docker official documentation, Docker Engine/Desktop release notes, Docker Compose documentation, Ubuntu package information, and the Podman official site. Docker installation commands and package names can change by distribution and version, so use the official docs as the final check before changing a real server.

In this article

Background

A common deployment failure starts with a vague sentence: “It runs locally, but the server fails.” The cause is often a difference in OS packages, runtime versions, environment variables, network ports, file paths, or manually edited server state.

Docker addresses this by building an image that contains the files and settings needed to start the application, then running that image as a container. For development, CI/CD, and repeatable test environments, this gives teams a clearer unit to build, run, stop, and replace.

  • Development and production environments can be described more consistently.
  • Application startup becomes easier to automate.
  • CI/CD pipelines can build and test the same image shape repeatedly.
  • Multiple services can be started together with Docker Compose.

What was checked

Source checked What it affects in this guide
Docker concept docs Image, container, registry, Dockerfile, and Compose explanations
Docker Engine Ubuntu install docs Official APT repository flow and docker-ce package family
Docker Engine release notes Current Engine release line and the need to verify version-specific behavior
Docker Desktop release notes Desktop updates may roll out gradually and older versions may not always be available
Docker Compose docs Modern Compose is commonly used as docker compose, a Docker CLI plugin
Ubuntu package information docker.io exists as a separate Ubuntu distribution package
Podman official site Podman is a Docker-adjacent alternative worth comparing in some Linux/server contexts

Docker concepts and installation

What is Docker?

Docker is a container-based application platform. A container runs an application process in an isolated environment, but unlike a full virtual machine it does not boot a separate guest operating system for every application.

Term Meaning
Image A read-only template containing files, libraries, and configuration for an application
Container A running instance created from an image
Docker Engine The runtime that creates and runs containers
Docker CLI The command-line tool used for commands such as docker run, docker build, and docker ps
Registry A repository for storing and distributing images, such as Docker Hub
Docker Compose A tool for defining multiple container services in a YAML file and running them together

Docker containers vs. virtual machines

Area Virtual machine Docker container
Isolation model Runs a guest OS on a hypervisor Shares the host OS kernel and isolates processes
Startup Usually slower Usually faster
Resource use Heavier Lighter for many application workloads
Typical use OS-level isolation and strong VM boundaries Application packaging, deployment, and consistent dev environments
Examples VMware, VirtualBox, Hyper-V Docker, Podman

This does not mean a container is automatically safer than a VM. Containers share the host kernel, so production use still requires careful checks around permissions, mounted directories, exposed ports, image trust, and Docker socket access.

Installing Docker Engine on Ubuntu

On Ubuntu, Docker’s official documentation recommends adding Docker’s APT repository and installing the Docker Engine packages from there. Ubuntu’s own docker.io package can also exist, but when you want the official Docker Engine, Buildx, and Compose plugin flow, the official repository is often the clearer path.

Package path What it means When it fits
docker-ce Docker Community Edition packages from Docker’s official repository You want to follow Docker’s current official install guide and plugin package names
docker.io Docker package from Ubuntu’s distribution repository Your organization intentionally follows Ubuntu distribution packages and update policy

Before installing on an existing server, check whether old packages such as docker.io, docker-compose, docker-compose-v2, podman-docker, containerd, or runc are already present. Docker’s docs list conflicting packages, but on a real server you should check running containers and package dependencies before removing anything.

# 1. Update package metadata and install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl

# 2. Create the keyring directory
sudo install -m 0755 -d /etc/apt/keyrings

# 3. Add Docker's official GPG key
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

# 4. Register Docker's official APT repository
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

# 5. Update package metadata again
sudo apt update

# 6. Install Docker Engine and related plugins
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Then verify the installation:

sudo docker run hello-world

If the installation is normal, Docker downloads a test image and prints a Hello from Docker! message.

Running Docker without sudo

On Linux, you can add your current user to the docker group if you do not want to type sudo for every Docker command.

sudo usermod -aG docker $USER
newgrp docker

docker run hello-world

This is convenient for a personal development machine, but it is not a harmless permission change. Docker group access can effectively provide high privileges on the host. Treat a production server differently from a local laptop.

Docker Desktop on Windows and macOS

On Windows and macOS, most beginners install Docker Desktop. It bundles Docker Engine, Docker CLI, Docker Compose, and GUI management features. The important caveat is that Windows/macOS do not run Linux containers in exactly the same way as a native Linux server; Docker Desktop uses a virtualization layer, and Windows commonly uses a WSL 2 based environment.

docker version
docker run hello-world

Basic Docker commands

# Run an Ubuntu container with an interactive shell
docker run -it ubuntu bash

# Show running containers
docker ps

# Show all containers
docker ps -a

# Show images
docker images

# Stop and remove a container
docker stop <container_id>
docker rm <container_id>

# Remove an image
docker rmi <image_id>

Image and container execution flow

Write a Dockerfile
  ↓
Build an image with docker build
  ↓
Run a container with docker run
  ↓
Check state with docker logs / docker exec
  ↓
Clean up with docker stop / docker rm
docker run -d --name sample-nginx -p 8080:80 nginx:stable
curl http://localhost:8080

docker stop sample-nginx
docker rm sample-nginx

A simple Dockerfile example

FROM nginx:stable
COPY ./index.html /usr/share/nginx/html/index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Docker Test</title>
</head>
<body>
  <h1>Hello Docker</h1>
</body>
</html>
docker build -t sample-nginx-page .
docker run -d --name sample-page -p 8080:80 sample-nginx-page
curl http://localhost:8080

docker stop sample-page
docker rm sample-page

Docker Compose example

With Docker Compose, multiple services can be described in one YAML file. In current Docker environments, the common command shape is docker compose, not always the older standalone docker-compose binary.

services:
  postgres:
    image: postgres:16
    container_name: sample-postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: sampledb
      POSTGRES_USER: sampleuser
      POSTGRES_PASSWORD: "****"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
docker compose up -d
docker compose down

# Be careful: this removes the named volume and deletes persisted database data.
docker compose down -v

Best practices

Use official and trusted images

Docker Hub contains many images, but not every image is maintained, safe, or appropriate for production. Start with official images or trusted vendor images whenever possible.

Pin image tags clearly

Avoid relying only on latest in production-like environments. A clear tag such as postgres:16 reduces unexpected upgrades.

Separate environment-specific configuration

Do not bake every environment-specific value into the image. Put database hosts, ports, modes, and similar settings into Compose environment values, .env files, or your deployment system’s configuration mechanism. For sensitive values, use a secrets mechanism suitable for your environment.

Manage volumes and networks deliberately

If a container stores data that must survive container replacement, use a volume or external storage. If multiple services communicate through Compose, understand that service names can be used as network names inside the Compose network.

Review security before production use

  • Container user and privilege level
  • Host directory mount scope
  • Docker socket exposure
  • Image vulnerability scanning
  • Which ports are published to the host or internet
  • Logging and monitoring

Compare Docker and Podman when policy requires it

Docker is the most familiar starting point for many developers, especially with Docker Desktop and Compose-based local development. In Linux server environments, rootless containers, daemonless operation, or organizational security policy may make Podman worth comparing.

Area Docker Podman
Usability Large Docker Desktop, Docker Engine, and Compose ecosystem Docker-like CLI experience
Daemon model Centered on the Docker daemon Known for a daemonless model
Good fit Local development standardization, Compose workflows, Docker Hub ecosystem Rootless container workflows, Linux-server-centered operation, specific security policies

Common mistakes and troubleshooting

Confusing docker.io and docker-ce

If a guide says apt install docker.io but Docker’s official docs say docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin, do not treat them as identical paths. First decide whether you are following Ubuntu’s package policy or Docker’s official repository.

Adding every user to the docker group without thinking

Adding a user to the docker group fixes many local permission errors, but it can also grant powerful host access. On a server, check the permission policy before applying the convenience fix.

Treating containers like manually maintained VMs

If you keep entering a container and editing files by hand, the environment becomes hard to reproduce. Prefer Dockerfiles, Compose files, environment variables, and rebuildable images.

Keeping persistent data only inside the container

A container can be removed and recreated. Databases, uploads, and other persistent data should live in volumes or external storage.

FAQ

What should I do if Docker on Ubuntu says permission denied?

If you see permission denied while trying to connect to the Docker daemon socket, the current user probably cannot access the Docker daemon socket. For a local development machine, adding the user to the docker group and starting a new login session can fix it.

sudo usermod -aG docker $USER
newgrp docker

What should I check when docker compose does not work?

Check whether the Compose CLI plugin is installed.

docker compose version
sudo apt install docker-compose-plugin

Can I use Docker on a Linux server without Docker Desktop?

Yes. Linux servers commonly use Docker Engine and the Compose plugin directly. Docker Desktop is mainly a convenient developer tool for Windows/macOS environments.

How do I verify that Docker is installed?

docker version
docker run hello-world

docker run hello-world is the simplest functional check because it verifies that Docker can pull an image and run a container.

Conclusion

Docker is useful because it turns an application runtime into something you can build, run, stop, and recreate. On Ubuntu, the official Docker repository path is often the clearest way to install Docker Engine and the Compose plugin; on Windows and macOS, Docker Desktop is the usual beginner-friendly route.

The key is not only installing Docker successfully. Check package source, permissions, image tags, volumes, published ports, and production security settings before treating a local example as an operations-ready setup.

References

Original Korean version: This article is based on the Korean version and lightly adapted for English readers.
Read the original Korean post.

Please show some love to Korean, too.