Будем делать всё руками, без Docker Desktop и прочей магии.

Что получим в итоге

После выполнения инструкции у нас будет сервер примерно такой конфигурации:

Debian 13
   │
   ├── Docker Engine
   │
   ├── Docker Compose
   │
   ├── nginx
   │
   ├── PostgreSQL
   │
   └── приложение

Все сервисы будут изолированы друг от друга и управляться через docker compose.

1. Подготовка Debian 13

Для начала обновляем систему:

apt update
apt full-upgrade -y

Устанавливаем необходимые пакеты:

apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

Проверяем версию операционной системы:

cat /etc/debian_version
cat /etc/os-release

Должно быть что-то вроде:

PRETTY_NAME="Debian GNU/Linux 13 (trixie)"

2. Установка Docker

Я предпочитаю устанавливать Docker из официального репозитория, а не из случайных скриптов из интернета.

Создаём каталог для ключей:

install -m 0755 -d /etc/apt/keyrings

Скачиваем ключ репозитория:

curl -fsSL https://download.docker.com/linux/debian/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/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  > /etc/apt/sources.list.d/docker.list

Обновляем список пакетов:

apt update

Проверяем, откуда будет установлен Docker:

apt-cache policy docker-ce

Теперь устанавливаем:

apt install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin

Проверяем:

docker --version
docker compose version

Если всё нормально, получим версии Docker Engine и Docker Compose.

3. Проверяем работу Docker

Запускаем тестовый контейнер:

docker run hello-world

Если Docker установлен правильно, увидим сообщение:

Hello from Docker!

Проверяем запущенные контейнеры:

docker ps

А вообще все контейнеры:

docker ps -a

4. Разрешаем Docker запускаться автоматически

Проверяем состояние:

systemctl status docker

Включаем автозапуск:

systemctl enable docker

И запускаем:

systemctl start docker

Можно сделать одной командой:

systemctl enable --now docker

5. Работа с Docker без root

По умолчанию Docker требует root-доступ.

Это неудобно, особенно если Docker используется разработчиком или администратором не для системных операций.

Добавим пользователя в группу docker.

Например:

usermod -aG docker username

После этого необходимо перелогиниться.

Проверяем:

groups

В списке должна появиться группа:

docker

Важный момент

Группа docker фактически предоставляет пользователю очень высокие привилегии на системе.

Поэтому не стоит добавлять в неё всех пользователей подряд.

6. Docker Compose

Современный Docker уже содержит Compose как плагин.

Проверяем:

docker compose version

Обратите внимание на команду:

docker compose

а не старый вариант:

docker-compose

Для новых установок я рекомендую использовать именно современный вариант.

7. Первый docker-compose.yml

Создадим каталог проекта:

mkdir -p /opt/test-app
cd /opt/test-app

Создадим файл docker-compose.yml

services:

  nginx:
    image: nginx:alpine
    container_name: test-nginx
    restart: unless-stopped
    ports:
      - "8080:80"

Запускаем:

docker compose up -d

Проверяем:

docker compose ps

Должны увидеть контейнер: test-nginx

Теперь открываем http://IP_SERVERA:8080 Если всё прошло правильно, мы увидим стандартную страничку nginx

8. Полезные команды Docker

Это тот минимум, который реально приходится использовать постоянно.

Посмотреть запущенные контейнеры:

docker ps

Посмотреть вообще все контейнеры:

docker ps -a

Посмотреть образы:

docker images

Посмотреть логи:

docker logs test-nginx

Следить за логами в реальном времени:

docker logs -f test-nginx

Зайти внутрь контейнера:

docker exec -it test-nginx sh

Перезапустить:

docker restart test-nginx

Остановить:

docker stop test-nginx

Удалить:

docker rm test-nginx

Удалить образ:

docker rmi nginx:alpine

9. Запускаем PostgreSQL

Теперь сделаем что-то более полезное. Изменим docker-compose.yml

services:

  nginx:
    image: nginx:alpine
    container_name: test-nginx
    restart: unless-stopped
    ports:
      - "8080:80"

  postgres:
    image: postgres:17
    container_name: test-postgres
    restart: unless-stopped

    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: change_this_password

    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Запускаем и проверяем:

docker compose up -d
docker compose ps

Теперь у нас два контейнера:

test-nginx
test-postgres

10. Почему обязательно нужен volume

Самая распространённая ошибка новичка:

«Я запустил PostgreSQL в Docker, значит данные теперь хранятся в контейнере».

Нет.

Контейнер — это не место, где следует хранить критичные данные.

Если контейнер удалить:

docker rm test-postgres

то данные могут исчезнуть вместе с ним.

Поэтому для PostgreSQL используется volume:

volumes:
  - postgres_data:/var/lib/postgresql/data

Посмотреть volumes:

docker volume ls

Посмотреть информацию:

docker volume inspect test_postgres_data

Название volume может отличаться в зависимости от имени проекта.

11. Docker и firewall

Здесь есть важный нюанс.

Многие ставят ufw, запрещают входящие подключения и думают, что Docker автоматически будет соблюдать эти правила.

Не всё так просто.

Docker самостоятельно создаёт правила сетевой фильтрации для опубликованных портов.

Например:

ports:
  - "8080:80"

может сделать сервис доступным снаружи.

Поэтому перед публикацией контейнеров наружу нужно понимать, какие порты реально открываются.

Проверяем:

ss -lntup

Если PostgreSQL не должен быть доступен из интернета, не нужно делать:

ports:
  - "5432:5432"

Во многих случаях PostgreSQL вообще не требуется публиковать наружу.

Контейнеры внутри одной Docker-сети смогут обращаться к нему напрямую.

12. Как смотреть нагрузку

Docker предоставляет удобную команду:

docker stats

Получаем примерно:

CONTAINER       CPU %     MEM USAGE / LIMIT
test-nginx      0.01%     5MiB / 2GiB
test-postgres   0.15%     80MiB / 2GiB

13. Что не стоит делать в production

Есть несколько типичных ошибок.

Не использовать latest

Например:

image: postgres:latest

Лучше фиксировать версию:

image: postgres:17

Не запускать контейнеры от root без необходимости

Для готовых официальных образов это обычно уже продумано, но при создании собственных Dockerfile лучше явно разобраться с пользователем, правами и файловой системой.

14. Минимальный production-вариант

В итоге базовый Compose может выглядеть примерно так:

services:

  app:
    image: my-app:1.0
    container_name: my-app
    restart: unless-stopped

    networks:
      - backend

  postgres:
    image: postgres:17
    container_name: my-postgres
    restart: unless-stopped

    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: change_me

    volumes:
      - postgres_data:/var/lib/postgresql/data

    networks:
      - backend

networks:
  backend:

volumes:
  postgres_data:

Заключение

Docker на Debian 13 устанавливается довольно просто. Самое интересное начинается дальше — когда на сервере появляется несколько сервисов.

Например:

                    Internet
                       │
                       ▼
                    nginx
                       │
                       ▼
                 application
                  │         │
                  ▼         ▼
              PostgreSQL   Redis
                  │
                  ▼
                backup

Именно в таком виде Docker становится действительно полезным: каждый сервис живёт отдельно, конфигурация описывается кодом, сервер можно быстро восстановить, а перенос инфраструктуры на другую машину занимает гораздо меньше времени.

Для небольшого VPS или домашнего сервера Docker + Docker Compose уже позволяют собрать вполне серьёзную инфраструктуру.

А дальше можно добавить:

  • reverse proxy;
  • HTTPS;
  • автоматические backup PostgreSQL;
  • Prometheus + Grafana;
  • Loki для логов;
  • Watchtower или собственный процесс обновления;
  • GitLab CI/CD;
  • отдельную Docker-сеть для frontend/backend;
  • мониторинг состояния контейнеров.

И тогда обычный Debian-сервер превращается уже в полноценную небольшую инфраструктурную платформу.

Если статья оказалась полезной — сохраните её. Когда Docker в очередной раз перестанет запускаться в два часа ночи, эта ссылка может пригодиться.