Podman запускает контейнеры без постоянного демона и умеет работать от обычного пользователя. Но при переносе проекта с Docker почти сразу возникает вопрос: что делать с готовым compose-файлом, в котором описаны сервисы, тома и сети. Переписывать всё на длинные команды podman run не нужно. Есть утилита podman-compose и встроенная обёртка podman compose, которая вызывает выбранный провайдер. Оба варианта читают тот же YAML, но различия в поведении стоит проверить до вывода нагрузки в работу.
Два способа запустить compose-файл
podman-compose — отдельная утилита на Python, её ставят пакетом из репозитория. Команда podman compose входит в сам Podman и только передаёт аргументы внешнему провайдеру. Список провайдеров задаётся в containers.conf в секции [engine], параметр compose_providers. По умолчанию Podman ищет docker-compose, а затем podman-compose. Если на машине остался Docker CLI, легко получить неожиданный результат: команда выполнится, но контейнеры окажутся в Docker. Поэтому сначала выясняем, кто именно отвечает на вызов.
sudo apt install podman podman-compose
podman --version
podman-compose --version
podman compose version
Последняя команда показывает выбранный провайдер. Если в выводе упомянут docker-compose, провайдера задают явно в конфигурации или удаляют Docker CLI с узла, где он не нужен. Смешивать два движка на одном хосте можно, но диагностика усложняется: podman ps не увидит контейнеры Docker, и наоборот.
Подготовка compose-файла
Большинство ключей переносится без правок: image, ports, volumes, environment, healthcheck, restart. Именованные тома лучше не заменять привязкой к каталогу хоста: у rootless-контейнера другой набор UID, и права на файлы могут не совпасть. Для SELinux добавьте суффикс :Z к точке монтирования, иначе процесс получит отказ доступа.
services:
web:
image: docker.io/library/nginx:alpine
ports:
- "8080:80"
volumes:
- html:/usr/share/nginx/html:Z
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
volumes:
html:
Порт 80 внутри контейнера — нормальный приём, но публиковать его в rootless-режиме нельзя без отдельной настройки. Наружу отдаём 8080 и ставим впереди обратный прокси, либо разрешаем непривилегированные порты через sysctl. Второй путь проще для домашнего сервера, первый безопаснее для машины с доступом из сети.
Запуск и повседневные команды
Дальше работа почти не отличается от привычной. Обёртка поднимает проект в фоне, показывает состояние, отдаёт логи и запускает команду внутри контейнера. podman-compose дополнительно создаёт pod на проект, поэтому в podman pod ps виден общий контейнер инфраструктуры.
podman compose up -d
podman compose ps
podman compose logs -f web
podman compose exec web sh
Остановка с down удаляет контейнеры и сети, но сохраняет именованные тома. Ключ -v удаляет и их, поэтому применять его стоит осознанно. Перед запуском полезно выполнить podman compose config: команда раскрывает переменные окружения и показывает итоговый YAML, что помогает поймать опечатку в имени тома или незаполненную переменную.
Rootless, порты и SELinux
Проверьте диапазоны subordinate UID и GID в /etc/subuid и /etc/subgid. Если пользователь создан без них, Podman не сможет отобразить владельцев файлов внутри контейнера, и вы увидите странные числовые идентификаторы. Исправить это можно через usermod --add-subuids и --add-subgids, после чего полезно перезапустить сессию.
grep "$USER" /etc/subuid /etc/subgid
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/90-podman.conf
loginctl enable-linger "$USER"
Без enable-linger пользовательские процессы завершаются после выхода из SSH, и контейнеры останавливаются вместе с ними. Для серверного сценария это обязательный шаг. Ещё одна частая проблема — файлы, созданные контейнером, принадлежат отображённому UID; не меняйте владельца рекурсивно на хосте, а проверьте настройки тома и права каталога.
Автозапуск через systemd
Старый способ с podman generate systemd считается устаревшим, в Podman 5 и новее основная ставка сделана на Quadlet. Файл с расширением .container кладут в ~/.config/containers/systemd/, systemd генерирует unit при перезагрузке конфигурации. Для проекта из нескольких сервисов добавляют общую сеть и зависимости After=.
[Unit]
Description=Nginx for site
[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=html.volume:/usr/share/nginx/html:Z
AutoUpdate=registry
[Install]
WantedBy=default.target
Рядом должен лежать файл html.volume, иначе юнит не запустится. После создания выполните systemctl --user daemon-reload и запустите юнит. Параметр AutoUpdate=registry разрешает podman auto-update подтягивать новый образ; без него команда ничего не изменит. Обновление лучше ставить в таймер и проверять здоровье сервиса после каждого запуска.
Диагностика
Когда контейнер не стартует, смотрите не только логи приложения. Обёртка может скрывать причину, если ошибка произошла на этапе разбора файла или монтирования тома.
podman compose config— проверка синтаксиса и подстановки переменных.podman inspect— состояние healthcheck и код возврата.podman events— события движка за последние минуты.journalctl --user— запуск через systemd и причины падения юнита.
podman compose config
podman inspect --format '{{.State.Health.Status}}' web
podman events --filter container=web --since 10m
journalctl --user -u web --no-pager -n 50
Код 127 обычно означает, что в образе нет команды из healthcheck, например wget или curl. Ошибка address already in use указывает на занятый порт: проверьте ss -ltnp. Отказ доступа к тому часто связан с SELinux или неверным владельцем каталога, а не с самим Podman.
Ограничения подхода
Compose остаётся инструментом для одного хоста. Он не планирует задачи на кластер, не переносит контейнеры между машинами и не умеет автоматически масштабировать сервис. Ключи deploy поддерживаются частично, условия depends_on обрабатываются не во всех версиях, а сборка образов может отличаться от Docker. Для разработки и небольшого сервера этого достаточно, для отказоустойчивого кластера нужен другой уровень оркестрации.
Если приложение уже описано в compose-файле, перенос на Podman занимает минуты. Начните с проверки провайдера, затем настройте rootless-порты и linger, а автозапуск переведите на Quadlet. Такой набор даёт предсказуемый сервер без демона и без лишних зависимостей.
Итог
Podman Compose — рабочий способ запускать привычные многоконтейнерные проекты без Docker. Выберите провайдера, проверьте монтирования и порты, добавьте systemd-юнит для автозапуска. Ограничения известны заранее, поэтому решение не станет сюрпризом при росте нагрузки.