- Один сервис — одна папка — один
compose.yml - Все пароли и токены — в отдельный
.env, не в сам yml - Reverse proxy (Traefik или Nginx Proxy Manager) — единая точка входа вместо россыпи портов
- Обновление:
docker compose pull && docker compose up -d, или Watchtower для автомата - Portainer — опционально для GUI, но compose-файлы держи на диске, не в его базе
Диагноз: ты запустил три контейнера и уже запутался
Docker Compose для домашнего сервера — это когда всё работает, но ты не помнишь почему. Поднял Nextcloud командой из чужого туториала, потом добавил Jellyfin, потом Vaultwarden. Каждый запущен через docker run с портами, которые ты уже не помнишь. Перезагрузил сервер — половина не стартовала сама. Знакомо?
Это не проблема Docker. Это проблема того, что ты пропустил шаг «как это организовать» и сразу прыгнул в «как это запустить». Шаг неочевидный, потому что большинство туториалов заканчиваются на docker run -d -p 8080:80 и пожелании удачи.
В этой статье разберём всё, что нужно для нормальной домашней инфраструктуры на Docker Compose:
- как структурировать compose-проекты так, чтобы через полгода не разбираться с нуля
- reverse proxy — одна точка входа вместо портов 8080, 8081, 8082, 8083…
- хранение секретов без паролей в открытом тексте
- обновление контейнеров — ручное и автоматическое
- Portainer против чистого CLI — что реально нужно, а что нет
Время на настройку с нуля: 2-4 часа. Что нужно: сервер или мини-ПК с Ubuntu/Debian, установленный Docker Engine и Docker Compose v2, базовые навыки работы с терминалом.
| Компонент | Минимум | Рекомендуется |
|---|---|---|
| ОС | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS / Debian 12 |
| Docker Engine | 24.x | 26.x и выше |
| Docker Compose | v2.20 | v2.27 и выше |
| RAM | 4 GB | 8 GB и выше |
| Диск | 32 GB SSD (ОС) | SSD под ОС + HDD под данные |
На момент публикации актуальна версия Docker Engine 26.1 и Compose v2.27. Перед установкой проверь свежие релизы на docs.docker.com.
Почему Docker Compose стал стандартом homelab-инфраструктуры
Раньше домашние серверы держались на скриптах. Bash-скрипт на 200 строк, который запускает всё при старте, и документация в виде заметки «не трогай это, оно само работает». Весело.
Docker Compose решает три конкретные проблемы. Первая: воспроизводимость. Один YAML-файл описывает весь стек — образы, порты, переменные окружения, тома, сети. Скопировал папку на новый сервер, запустил docker compose up -d — всё поднялось. Вторая: читаемость. Через полгода ты открываешь compose-файл и за 30 секунд понимаешь, что там запущено и почему. Третья: управление жизненным циклом. Остановить, обновить, перезапустить стек — одна команда.
Infrastructure as code в самом приземлённом смысле: конфиги в Git, изменения видны в diff, можно откатиться. Для homelab это избыточно звучит, но когда ты в третий раз по памяти пересобираешь стек после переезда на новое железо — начинаешь ценить.
Как правильно организовать compose-проекты
Один compose-файл или несколько?
Видел оба подхода. Один гигантский docker-compose.yml на 400 строк со всеми сервисами — это технически работает и практически невыносимо. Добавить новый сервис, убрать старый, разобраться в зависимостях — всё превращается в хирургию.
Правильная структура: один сервис — одна папка — один compose.yml. Папки хранятся в едином каталоге, например /opt/docker/ или /home/user/docker/. Структура выглядит так:
/opt/docker/
├── traefik/
│ ├── compose.yml
│ ├── .env
│ └── config/
│ └── traefik.yml
├── nextcloud/
│ ├── compose.yml
│ ├── .env
│ └── data/
├── jellyfin/
│ ├── compose.yml
│ ├── .env
│ └── config/
├── vaultwarden/
│ ├── compose.yml
│ ├── .env
│ └── data/
└── monitoring/
├── compose.yml
├── .env
└── grafana/
Каждая папка — независимый проект. Можно остановить Jellyfin, не трогая Nextcloud. Можно обновить один стек без риска сломать другой. Отдельный .env на каждый проект — пароли не перемешиваются.
Обрати внимание на название файла. Официально с Compose v2 рекомендуется compose.yml (без docker- префикса). Старое название docker-compose.yml поддерживается, но это legacy.
Именование проектов и сети
По умолчанию Docker Compose берёт имя проекта из имени папки. Папка называется nextcloud — проект называется nextcloud, контейнеры называются nextcloud-app-1, nextcloud-db-1. Предсказуемо и удобно.
Для reverse proxy нужна общая сеть, которая соединяет все проекты. Создай её один раз:
docker network create proxy
Теперь в каждом compose-файле подключай сервисы к этой сети:
services:
app:
image: nextcloud:latest
networks:
- proxy
- internal
networks:
proxy:
external: true
internal:
driver: bridge
Сеть proxy — внешняя, через неё reverse proxy видит контейнеры. Сеть internal — внутренняя для этого стека, через неё приложение общается с базой данных. База данных торчит только в internal и снаружи недоступна вообще.
Тома: где хранить данные
Два варианта: именованные тома Docker и bind mounts (прямые пути на диске). Для homelab я предпочитаю bind mounts — папка данных лежит рядом с compose-файлом, бэкапить просто, содержимое видно без лишних команд.
services:
jellyfin:
image: jellyfin/jellyfin:latest
volumes:
- ./config:/config
- ./cache:/cache
- /mnt/media:/media:ro
./config — конфиги Jellyfin, лежат рядом с compose.yml. /mnt/media — медиатека на отдельном диске, подключена read-only. Случайно перезаписать контентом через контейнер не получится.
Reverse proxy для Docker Compose
Без reverse proxy домашний сервер выглядит так: Nextcloud на порту 8080, Jellyfin на 8096, Vaultwarden на 8222, Grafana на 3000. Запомнить реально, но объяснить жене какой порт для чего — уже нет. И SSL для каждого порта отдельно — это боль.
Reverse proxy закрывает эту проблему: один вход на 80/443, маршрутизация по домену или пути, SSL автоматически через Let’s Encrypt. nextcloud.home.lab вместо 192.168.1.100:8080.
Traefik или Nginx Proxy Manager
| Критерий | Traefik v3 | Nginx Proxy Manager | Caddy |
|---|---|---|---|
| Сложность настройки | Высокая | Низкая (GUI) | Средняя |
| Автообнаружение контейнеров | Да (через labels) | Нет (ручная настройка) | Нет |
| Let’s Encrypt | Автоматически | Автоматически | Автоматически |
| Dashboard | Есть | Есть (весь UI) | Нет |
| Middlewares (auth, ratelimit) | Богатые | Базовые | Плагины |
| Для новичка | Сложновато | Самое то | Хорошо |
Новичку — Nginx Proxy Manager. Ты кликаешь в GUI, вводишь домен, указываешь IP:порт контейнера, ставишь галочку SSL — всё. Traefik нужен когда хочется автоматизации: добавил контейнер с нужными labels — маршрут появился сам, без ручной настройки.
Я покажу Traefik, потому что он лучше масштабируется и лучше подходит для infrastructure as code. Но выбор за тобой.
Traefik: минимальная рабочая конфигурация
Структура файлов:
/opt/docker/traefik/
├── compose.yml
├── .env
└── config/
├── traefik.yml
└── acme.json # создать пустым, chmod 600
Сначала создай пустой файл для сертификатов:
touch /opt/docker/traefik/config/acme.json
chmod 600 /opt/docker/traefik/config/acme.json
Статическая конфигурация traefik.yml:
api:
dashboard: true
insecure: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: proxy
certificatesResolvers:
letsencrypt:
acme:
email: "your@email.com"
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: web
Файл .env для Traefik:
TRAEFIK_DASHBOARD_USER=admin
TRAEFIK_DASHBOARD_PASSWORD_HASH=$$apr1$$xyz$$hashedpassword
DOMAIN=home.example.com
Хеш пароля для dashboard генерируй так:
echo $(htpasswd -nb admin yourpassword) | sed -e s/\\$/\\$\\$/g
Compose-файл Traefik:
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config/traefik.yml:/etc/traefik/traefik.yml:ro
- ./config/acme.json:/etc/traefik/acme.json
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_DASHBOARD_USER}:${TRAEFIK_DASHBOARD_PASSWORD_HASH}"
networks:
proxy:
external: true
Запускай:
cd /opt/docker/traefik
docker compose up -d
docker compose logs -f traefik
Подключение сервиса к Traefik
Теперь добавить любой сервис в reverse proxy — это просто labels в его compose-файле. Пример с Vaultwarden:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./data:/data
env_file: .env
networks:
- proxy
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`vault.${DOMAIN}`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
networks:
proxy:
external: true
internal:
driver: bridge
Добавил labels, сделал docker compose up -d — Traefik подхватил контейнер автоматически. Маршрут vault.home.example.com появился без перезапуска Traefik. Вот за это его и любят.
Secrets и безопасность контейнеров
Где хранить .env файлы
Самая частая ошибка: пароли прямо в compose.yml. Файл попадает в Git, Git куда-нибудь пушится, пароль живёт вечно в истории коммитов. Привет, утечка.
Правило простое: всё секретное — в .env, .env — в .gitignore. Compose автоматически подхватывает .env из той же папки.
# .env для Nextcloud
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD=сюда_нормальный_пароль_не_admin123
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=и_сюда_тоже
NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.home.example.com
В compose.yml используешь переменные:
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
networks:
- internal
Или короче через env_file:
services:
app:
image: nextcloud:latest
env_file: .env
Нужно ли использовать Docker Secrets
Docker Secrets — нативный механизм безопасного хранения чувствительных данных. Секрет хранится в зашифрованном виде и монтируется внутрь контейнера как файл по пути /run/secrets/имя_секрета. Звучит хорошо.
Проблема в том, что нативные Docker Secrets полноценно работают только в Docker Swarm. В обычном Compose (без Swarm) это костыль через bind-mount файла с диска. Реального шифрования нет — файл с паролем лежит на хосте в открытом виде.
Для homelab на одной машине без Swarm разница между .env файлом с правами 600 и Docker Secrets через file практически нулевая. Если файловая система хоста скомпрометирована — оба варианта одинаково уязвимы.
Что реально работает в Compose без Swarm:
# Создать файл с секретом
echo -n "supersecretpassword" > /opt/docker/nextcloud/secrets/db_password.txt
chmod 600 /opt/docker/nextcloud/secrets/db_password.txt
# В compose.yml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Образ должен поддерживать _FILE суффикс для переменных окружения. Postgres, MariaDB, официальные образы WordPress — поддерживают. Многие self-hosted проекты — нет, придётся смотреть документацию.
Итого: для домашнего сервера .env с правами 600 — нормальное решение. Docker Secrets через файл добавляет слой порядка, но не магической безопасности. HashiCorp Vault — если хочется по-настоящему, но это уже отдельная история.
Базовые меры безопасности
| Мера | Что делает | Как применить |
|---|---|---|
| read_only: true | Файловая система контейнера только для чтения | Добавить в сервис в compose.yml |
| no-new-privileges: true | Процесс не может получить новые привилегии | security_opt: ["no-new-privileges:true"] |
| user: «1000:1000» | Контейнер запускается не от root | Проверь, что образ поддерживает non-root |
| Отдельная internal сеть | БД недоступна снаружи стека | Две сети: proxy и internal |
| UFW на хосте | Закрыть порты кроме 80, 443, 22 | ufw allow 80,443,22/tcp |
Как обновлять контейнеры без боли
Ручное обновление: три команды
Самый безопасный способ. Ты контролируешь что и когда обновляется. Для продакшн-сервера — единственно правильный подход. Для домашнего — решай сам насколько тебе важна свежесть версий против стабильности.
cd /opt/docker/nextcloud
docker compose pull
docker compose up -d
docker image prune -f
docker compose pull скачивает новые образы, но не перезапускает контейнеры. docker compose up -d пересоздаёт только те контейнеры, для которых образ изменился. docker image prune -f удаляет старые образы, которые теперь висят без тега.
Обновить все стеки сразу скриптом:
#!/bin/bash
DOCKER_DIR="/opt/docker"
for dir in "$DOCKER_DIR"/*/; do
if [ -f "${dir}compose.yml" ]; then
echo "=== Обновляю: $dir ==="
cd "$dir" && docker compose pull && docker compose up -d
fi
done
docker image prune -f
echo "=== Готово ==="
Сохрани в /usr/local/bin/update-docker-stacks, дай права на выполнение, запускай когда удобно.
Автообновление через Watchtower
Watchtower смотрит за контейнерами, замечает обновлённые образы и автоматически перезапускает. Для homelab где живёт Jellyfin или Vaultwarden — удобно. Для Nextcloud с базой данных — осторожно: мажорные обновления могут требовать ручной миграции.
# /opt/docker/watchtower/compose.yml
services:
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_SCHEDULE: "0 0 4 * * *"
WATCHTOWER_NOTIFICATIONS: shoutrrr
WATCHTOWER_NOTIFICATION_URL: "telegram://token@telegram?channels=chatid"
Расписание в формате cron: 0 0 4 * * * — каждый день в 4 утра. WATCHTOWER_CLEANUP: true удаляет старые образы. Уведомления в Telegram — опционально, но приятно знать что произошло.
Если хочешь исключить конкретный контейнер из автообновления — добавь label:
labels:
- "com.centurylinklabs.watchtower.enable=false"
Для Nextcloud, баз данных и всего где важны миграции — ставь это label и обновляй руками. Проснуться утром с нерабочим Nextcloud потому что Watchtower выкатил мажорную версию — незабываемый опыт. Один раз достаточно.
Portainer vs чистый Docker CLI
Что такое Portainer и зачем он нужен
Portainer — веб-интерфейс для управления Docker. Видишь список контейнеров, можешь стартовать, останавливать, смотреть логи, лезть в терминал контейнера прямо из браузера. Для человека который боится терминала — отличный вход в Docker.
Для человека который терминала не боится — спорная ценность.
Почему многие уходят от Portainer
Я видел несколько сценариев миграции «с Portainer на CLI». Паттерн один: человек начал с Portainer, насоздавал стеки через GUI, потом захотел перенести на новый сервер и выяснил, что у него нет нормальных compose-файлов. Только скриншоты настроек в Portainer.
Второй момент — безопасность. Portainer цепляется к Docker socket (/var/run/docker.sock). Кто имеет доступ к этому сокету — тот имеет root на хосте. Portainer с веб-интерфейсом наружу плюс слабый пароль — приятный подарок для сканеров портов.
Альтернативы которые набирают популярность:
| Инструмент | Что умеет | Подходит для |
|---|---|---|
| Dockge | Веб-UI для compose-стеков, файлы на диске | Тех кто хочет GUI без потери файлов |
| Lazydocker | TUI в терминале, мониторинг и логи | Тех кто сидит в SSH |
| Dozzle | Только просмотр логов, легковесный | Быстро посмотреть что происходит |
| Чистый CLI | Всё через командную строку | Кто читает man перед тем как спросить |
Dockge — интересная альтернатива для тех кому GUI нужен. Он работает с compose-файлами прямо на диске, не прячет их в базу данных. Файлы остаются в своих папках и доступны для Git и скриптов.
Когда Portainer всё же оправдан
Если управляешь несколькими серверами из одного места — Portainer умеет это и делает хорошо. Если нужно дать коллеге или члену семьи доступ к перезапуску контейнеров без SSH-доступа на сервер — тоже ок. Просто держи его за Traefik с нормальной аутентификацией и не деплой через него стеки которые планируешь версионировать.
Типичная структура домашнего Docker-сервера
Вот как выглядит нормально организованный homelab к тому времени когда там живёт 10+ сервисов:
/opt/docker/
├── traefik/ # reverse proxy, точка входа
├── authelia/ # опционально: 2FA для всего
├── nextcloud/ # облачное хранилище
├── jellyfin/ # медиасервер
├── vaultwarden/ # менеджер паролей
├── gitea/ # приватный Git
├── paperless/ # хранилище документов
├── monitoring/ # Grafana + Prometheus + Loki
├── homer/ # стартовая страница с ссылками
└── watchtower/ # автообновление
Каждая папка — независимый compose-проект. Все завязаны на общую сеть proxy. Traefik раздаёт SSL и маршрутизирует по поддоменам. Watchtower обновляет некритичные сервисы ночью.
Бэкап простой: скопировать всю папку /opt/docker/ на внешний диск или в облако. Там лежат все конфиги и данные. Compose-файлы без данных держать в Git — для воспроизводимости.
# Простой скрипт бэкапа
#!/bin/bash
BACKUP_DIR="/mnt/backup/docker"
DATE=$(date +%Y-%m-%d)
tar -czf "${BACKUP_DIR}/docker-${DATE}.tar.gz" /opt/docker/
# Оставить только 7 последних бэкапов
ls -t "${BACKUP_DIR}"/docker-*.tar.gz | tail -n +8 | xargs -r rm
Профилактика и мониторинг
Автозапуск при старте сервера
restart: unless-stopped в каждом сервисе — контейнер перезапустится автоматически после перезагрузки сервера. Разница от restart: always: если ты остановил контейнер вручную, он не стартует сам при следующей перезагрузке. Удобно когда надо что-то поотлаживать.
services:
app:
image: jellyfin/jellyfin:latest
restart: unless-stopped
Мониторинг стека
Минимальный мониторинг для homelab — Grafana + Prometheus + cAdvisor. cAdvisor собирает метрики всех Docker-контейнеров, Prometheus их хранит, Grafana показывает.
# /opt/docker/monitoring/compose.yml
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
networks:
- proxy
- internal
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
volumes:
- grafana_data:/var/lib/grafana
env_file: .env
networks:
- proxy
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.${DOMAIN}`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
networks:
- internal
volumes:
prometheus_data:
grafana_data:
networks:
proxy:
external: true
internal:
driver: bridge
Типичные проблемы и решения
Контейнер не стартует после перезагрузки
Первым делом смотри логи:
docker compose logs имя_сервиса
# или последние 50 строк
docker compose logs --tail=50 имя_сервиса
Частые причины: переменная окружения не определена (опечатка в .env), том с неправильными правами, порт уже занят другим процессом.
Порт занят
# Кто занял порт 80
sudo ss -tlnp | grep :80
# или
sudo lsof -i :80
Traefik не выдаёт SSL-сертификат
Три причины в 90% случаев. Первая: домен не резолвится в IP твоего сервера. Проверь DNS. Вторая: порт 80 закрыт фаерволом — Let’s Encrypt не может пройти HTTP challenge. Третья: неправильные права на acme.json — должно быть 600.
# Проверить права
ls -la /opt/docker/traefik/config/acme.json
# Исправить если нужно
chmod 600 /opt/docker/traefik/config/acme.json
# Посмотреть логи Traefik
docker compose -f /opt/docker/traefik/compose.yml logs -f
Контейнеры из разных стеков не видят друг друга
Нужна общая сеть. Оба сервиса должны быть подключены к одной Docker-сети, и эта сеть должна быть помечена как external: true в обоих compose-файлах.
# Создать сеть
docker network create mynetwork
# Проверить к каким сетям подключён контейнер
docker inspect имя_контейнера | grep -A 20 Networks
Переменная окружения не подхватывается из .env
Файл .env должен лежать в той же папке откуда запускается docker compose. Если запускаешь compose из другой директории — явно укажи путь:
docker compose --env-file /opt/docker/nextcloud/.env -f /opt/docker/nextcloud/compose.yml up -d
Образ не обновляется, Watchtower ничего не делает
Watchtower обновляет только контейнеры запущенные с тегом :latest или конкретным тегом с новым дайджестом. Если закрепил версию типа image: nextcloud:28.0.1 — Watchtower не обновит, пока ты не поменяешь тег в compose-файле руками. Это на самом деле правильное поведение для Nextcloud.
Альтернативы Docker Compose
Docker Compose — не единственный вариант. Вот что ещё используют в homelab:
| Инструмент | Плюсы | Минусы | Подходит когда |
|---|---|---|---|
| Podman + Quadlets | Rootless по умолчанию, без демона, systemd-интеграция | Меньше образов, совместимость не 100% | Важна безопасность, Fedora/RHEL |
| Docker Swarm | Те же compose-файлы, несколько нод | Overkill для одной машины | Несколько серверов |
| K3s / Kubernetes | Промышленный стандарт, богатая экосистема | Сложность несопоставима с задачей | Обучение, не продакшн homelab |
| Nix / NixOS | Воспроизводимость на уровне ОС | Крутая кривая обучения | Если хочешь уйти глубже |
Для подавляющего большинства домашних серверов Docker Compose — оптимальный баланс между возможностями и сложностью. Podman интересен если принципиально важна безопасность rootless-контейнеров. Kubernetes в homelab — чаще обучение, чем практическая необходимость.
FAQ
Почему docker compose не запускается после перезагрузки сервера?
Проверь параметр restart в compose-файле. Без него контейнер не перезапустится автоматически. Добавь restart: unless-stopped во все сервисы и запусти docker compose up -d чтобы применить изменение. Если Docker-демон сам не запускается после перезагрузки — выполни sudo systemctl enable docker.
Как проверить что docker compose работает правильно?
Базовая диагностика: docker compose ps покажет статус всех сервисов стека. Статус Up — работает, Exit 1 или Restarting — что-то не так. Дальше docker compose logs имя_сервиса для деталей. Проверить сети: docker network ls и docker network inspect proxy.
Что делать если docker compose pull тянет старый образ?
Возможно образ закреплён конкретной версией в compose.yml или кеш. Попробуй docker compose pull --no-cache. Если в compose.yml прописан конкретный тег версии типа :28.0.1 — pull не поможет, нужно вручную поменять тег на новую версию или :latest.
Чем Dockge отличается от Portainer?
Ключевое отличие: Dockge хранит compose-файлы на диске как обычные файлы. Portainer хранит стеки в своей базе данных. Это означает что с Dockge можно редактировать файлы любым способом — через SSH, Git, текстовый редактор — и всё синхронизируется. С Portainer приходится работать только через его интерфейс, иначе рассинхронизация. Для homelab с Git-бэкапом конфигов Dockge намного удобнее.
Нужно ли использовать docker compose или docker-compose?
Docker Compose V1 (docker-compose, отдельный Python-пакет) устарел и не поддерживается с июля 2023 года. Используй Docker Compose V2 — это плагин к Docker CLI, вызывается как docker compose (без дефиса). Установи свежий Docker Engine и плагин Compose будет включён автоматически.
Итог
Если ты дочитал до сюда — у тебя теперь есть полный набор для нормального homelab на Docker Compose. Один сервис одна папка, общая сеть proxy, Traefik как точка входа, секреты в .env с правами 600, обновления через pull+up или Watchtower для некритичных сервисов. Вся инфраструктура описана файлами, переносится за полчаса, бэкапится одной командой tar.
Portainer — если хочется GUI, но compose-файлы держи на диске. Dockge — если нужен GUI и нормальный workflow с файлами. Чистый CLI — если не боишься терминала, а это скорее всего твой случай раз ты здесь.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →

