Docker Installation and Beginner's Guide: A Step-by-Step Tutorial

Introduction

To be honest, before I encountered Docker, I spent a considerable amount of time struggling with environment setup on servers—installing Java only to find the version was wrong, installing MySQL only to discover conflicts with system libraries...

Docker solves this problem. Simply put: it packages your application along with all its required environment into a "container," which can run on any machine. You'll never have to worry about the "it works on my machine" scenario again.

This tutorial will guide you from installing Docker from scratch, covering all the essential commands, to finally running a real multi-container project yourself.

1. What's the Difference Between Docker and a Virtual Machine?

Many beginners hear about containers and think, isn't this just a lightweight virtual machine? Actually, they are quite different:

Comparison Point Docker Container Virtual Machine
Startup Speed Seconds Minutes
Resource Usage Shares host kernel, very light Runs a full OS, very heavy
Performance Near-native Has overhead
Image Size Tens to hundreds of MB Often several GB

A virtual machine installs a complete new computer inside your computer, while Docker just creates an isolated "sandbox" for your application, sharing the host's kernel. That's why Docker starts faster and uses fewer resources.

2. Installing Docker

Debian / Ubuntu

One command does it all:

curl -fsSL https://get.docker.com | sudo sh

After installation, add the current user to the docker group so you don't need sudo for every docker command:

sudo usermod -aG docker $USER

You need to log out and back in for this to take effect; simply disconnect and reconnect your SSH session.

CentOS / Rocky Linux / AlmaLinux

Same installation script:

curl -fsSL https://get.docker.com | sudo sh

If CentOS reports a podman conflict, remove it first:

sudo yum remove -y podman buildah

Then run the installation command again.

Configuring Mirror Sources for Servers in China

If your server is in China, downloading images from Docker Hub will likely time out or fail completely. The reason is that since June 2024, the GFW has implemented SNI blocking on docker.com and related domains—even if DNS resolution works, the TLS connection is identified by the firewall and disconnected. At the same time, major domestic mirror acceleration services like Alibaba Cloud and Tencent Cloud have also gradually shut down their Docker Hub proxies due to "policy and regulatory requirements." So now, without configuring a mirror source, it's basically impossible to pull images on servers in China:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me"
  ]
}
EOF

Restart Docker for the configuration to take effect:

sudo systemctl daemon-reload
sudo systemctl restart docker

3. Verifying Installation

docker --version

Output similar to Docker version 28.x.x means it's installed correctly.

Run a test container to confirm Docker can pull images and run containers normally:

docker run --rm hello-world

If you see Hello from Docker!, everything is working perfectly 🎉

4. Setting Up Auto-start on Boot

sudo systemctl enable docker

Check the running status:

sudo systemctl status docker

It's normal if it shows Active: active (running).

5. Three Essential Concepts to Understand

Before using Docker, you just need to understand three terms:

Image: Think of it as an "installation package." For example, if you want to run Nginx, you download an Nginx image. Images are read-only and cannot be modified.

Container: When an image is run, it becomes a container. One image can run multiple containers simultaneously, each independent of the others. It's like installing the same software package on multiple computers.

Registry: A place where images are stored. Docker Hub is the largest public registry, hosting official images for most mainstream software.

6. Image-related Commands

Search for an image:

docker search nginx

Pull an image (defaults to the latest version):

docker pull nginx

Specify a version:

docker pull nginx:1.27

List locally available images:

docker images

Delete an image:

docker rmi nginx

If the image is still being used by a container, force delete it:

docker rmi -f nginx

7. Container-related Commands

Create and Run

docker run -d --name my-nginx -p 8080:80 nginx

Parameter explanation:

After running, visit http://server-ip:8080. If you see the Nginx welcome page, you're good.

Viewing Containers

# Only see running containers
docker ps

# See all (including stopped ones)
docker ps -a

Starting/Stopping Containers

docker stop my-nginx    # Stop
docker start my-nginx   # Start
docker restart my-nginx # Restart

Executing Commands Inside a Container

docker exec -it my-nginx bash

Once inside, it's like being in a mini Linux system. Use exit to leave.

Viewing Logs

docker logs my-nginx

# Real-time tailing
docker logs -f my-nginx

Deleting Containers

docker stop my-nginx
docker rm my-nginx

If you're too lazy for two steps, force delete:

docker rm -f my-nginx

8. Data Persistence: Mounting Directories

Containers are temporary by default; deleting them erases everything. To save data, you need to mount a host directory into the container:

docker run -d --name my-nginx \
  -p 8080:80 \
  -v /home/www:/usr/share/nginx/html \
  nginx

-v /home/www:/usr/share/nginx/html synchronizes the host's /home/www directory with the container's web directory bidirectionally. Changes on the host take effect immediately in the container, and vice versa. This way, even if the container is deleted and recreated, the data remains on the host.

9. Hands-on: Running MySQL with Docker

docker run -d --name my-mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=YourStrongPassword123 \
  -e MYSQL_DATABASE=mydb \
  -v /data/mysql:/var/lib/mysql \
  mysql:8.0

Wait a dozen seconds for MySQL to initialize, then test it:

docker exec -it my-mysql mysql -uroot -p

After entering the password, if you see the mysql> prompt, it's running. Type exit to leave.

10. Docker Compose: Managing Multiple Containers Together

Projects usually involve more than one container, and manually running docker run for each is tedious. Docker Compose uses a YAML file to describe all containers and can start them all with a single command.

First, check if Compose is installed:

docker compose version

If not:

sudo apt install docker-compose-plugin -y

Writing a docker-compose.yml

Take WordPress + MySQL as an example:

mkdir -p /home/myapp && cd /home/myapp
nano docker-compose.yml

Content:

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "80:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: wp_password
    volumes:
      - db_data:/var/lib/mysql

volumes:
  wp_data:
  db_data:

Press Ctrl+O then Enter to save, and Ctrl+X to exit.

One-command Startup

docker compose up -d

Both containers are automatically created and configured with networking. Visit http://server-ip to see the WordPress installation page.

Common commands:

docker compose ps          # Check status
docker compose logs -f     # View logs
docker compose down        # Stop and remove containers (data remains)
docker compose down -v     # Stop and remove containers + data (Use with caution!)

11. Common Issues

Port Already in Use

sudo lsof -i :8080

See what's using it, then either change the mapped port or stop that process.

Container Exits Immediately After Starting

docker logs container_name

Check the logs. It's usually due to incorrect configuration, port conflicts, or permission issues.

Disk Full

Docker accumulates unused images and cache over time. Clean it up:

docker system df       # First, see how much is used
docker system prune -af  # Clean all unused resources (Warning: Irreversible)

12. Security Recommendations

  1. Remember to update images regularly. Official images receive ongoing security patches.
  2. Don't expose ports for internal services to the outside world. Services like databases only need to communicate within the Docker internal network.
  3. Limit container resource usage to prevent a runaway container from crashing the entire machine.
docker run -d --name my-app --memory=512m --cpus=1 nginx
  1. Don't write passwords in plain text in commands. Remember to change passwords in published docker-compose.yml files to environment variables.

Summary

Getting started with Docker isn't actually difficult. The core process is just three steps: pull an image, run a container, mount a directory. Compose simply writes these steps into a file for unified management.

After finishing this tutorial, your next steps could be:

  1. Write your own Dockerfile to package your application into an image.
  2. Use Compose to set up a complete service stack (e.g., blog + database + Redis + Nginx).
  3. Learn about Docker's network modes (advanced).

Once you get into the habit, you can deploy almost anything with a single command, never having to manually set up environments again.
**,防止某个失控的容器把整台机器搞崩

docker run -d --name my-app --memory=512m --cpus=1 nginx
  1. 密码别明文写在命令里,发布出去的 docker-compose.yml 里的密码记得改成环境变量

Summary

Getting started with Docker isn't actually difficult. The core process is just three steps: pull an image, run a container, mount a directory. Compose simply writes these steps into a file for unified management.

After finishing this tutorial, your next steps could be:

  1. Write your own Dockerfile to package your application into an image.
  2. Use Compose to set up a complete service stack (e.g., blog + database + Redis + Nginx).
  3. Learn about Docker's network modes (advanced).

Once mastered, you can deploy almost anything with a single command, never having to manually set up environments again.