BuildKit и Buildah решают одну задачу: превращают описание сборки и файловый контекст в контейнерный образ. Оба инструмента работают с OCI-совместимыми образами, поддерживают непривилегированный запуск и подходят для автоматизации. Однако их архитектура, модель кэширования и интеграция с контейнерными движками заметно различаются.
Выбор обычно определяется не формальной поддержкой OCI, которая есть у обоих решений, а окружением. Для Docker-проектов естественным вариантом остаётся BuildKit через Docker Buildx. В стеке Podman удобнее Buildah или команда podman build, использующая код Buildah. На отдельном Linux VDS дополнительно важны доступность пользовательских пространств имён, файловая система, место под кэш и возможность настроить subordinate UID и GID.
Для задач сборки с административным доступом потребуется VPS-сервер: на виртуальном хостинге, как правило, нельзя установить контейнерные инструменты, настроить user namespaces, FUSE и хранилище образов.
Краткий ответ: когда выбирать BuildKit, а когда Buildah
BuildKit лучше подходит, если:
- основным интерфейсом команды служат Docker CLI и Docker Buildx;
- нужен производительный переносимый кэш для повторных сборок;
- используются современные возможности Dockerfile:
RUN --mount, секреты и SSH-сокеты; - одной командой собираются образы для нескольких платформ;
- на VDS можно запустить постоянный builder и сохранять локальный кэш между сборками.
Buildah удобнее, если:
- инфраструктура основана на Podman и общем хранилище
containers/storage; - нежелателен постоянно работающий демон;
- rootless-сборка должна запускаться как обычный процесс Linux;
- помимо Containerfile нужны низкоуровневые команды для создания, изменения и фиксации образа;
- важна естественная работа с OCI-форматом и инструментами семейства Containers.
Нельзя считать Buildah просто «BuildKit без Docker», а BuildKit — «ускоренным Buildah». Buildah тесно связан с моделью локального контейнерного хранилища и OCI-инструментами. BuildKit исполняет граф сборки через отдельный сервис и делает особый акцент на параллелизме и кэше.
Как устроен BuildKit
BuildKit состоит из клиента и сборочного сервиса buildkitd. Клиентом может выступать самостоятельная команда buildctl, но чаще с BuildKit работают через docker build или docker buildx build. Dockerfile frontend преобразует инструкции файла в низкоуровневый граф LLB. Затем BuildKit определяет зависимости операций, выполняет независимые части параллельно и рассчитывает ключи кэша по содержимому и параметрам операций.
Разделение клиента и builder-сервиса даёт несколько вариантов размещения:
- builder внутри Docker Engine;
- отдельный контейнер с BuildKit;
- системный или пользовательский процесс
buildkitdна VDS; - удалённый builder, к которому подключается клиент;
- временный daemonless-запуск, при котором сервис существует только во время задания.
Постоянный builder особенно полезен на VDS: его внутренний кэш остаётся на диске и ускоряет последующие сборки. В эфемерном CI локальное состояние обычно исчезает после задания, поэтому кэш нужно экспортировать во внешнее либо сохраняемое между запусками хранилище. Для выбора подходящего экспортера полезен материал о registry-кэше BuildKit в CI.

Как устроен Buildah
Buildah не требует постоянно работающего центрального демона. Команда запускает сборку как обычный процесс, создаёт рабочие контейнеры для инструкций RUN и сохраняет слои в локальном хранилище Containers. Такой подход хорошо сочетается с Unix-моделью: жизненный цикл процесса Buildah совпадает с жизненным циклом сборки.
Высокоуровневая команда выглядит привычно:
buildah build --layers -t example-app:test .
Старое имя buildah bud также встречается в сценариях и документации. Помимо сборки по Containerfile, Buildah предоставляет отдельные команды для операций над образом: можно создать рабочий контейнер, выполнить в нём команду, скопировать файлы, изменить метаданные и зафиксировать результат. Это полезно для специальных конвейеров, но для обычного приложения Containerfile остаётся более понятным и воспроизводимым описанием.
Podman использует код проекта Buildah для команды podman build. Если Buildah и Podman запущены одним пользователем и используют одинаковую конфигурацию хранилища, собранный образ становится доступен Podman без промежуточного load. Rootless- и rootful-хранилища разделены: образ обычного пользователя не появляется автоматически у root и наоборот.
Rootless-сборка: сходства и различия
Rootless-режим уменьшает последствия компрометации сборочного процесса: операции выполняются без полномочий настоящего root на хосте. Однако rootless не означает отсутствия требований к изоляции. Сборщику по-прежнему нужны пользовательские и mount-пространства имён, возможность хранить слои, выполнять контейнерные процессы и настраивать сеть для инструкций RUN.
Rootless BuildKit
Для самостоятельного rootless-запуска BuildKit применяется RootlessKit. Демон работает от непривилегированного пользователя, а buildctl подключается к его пользовательскому Unix-сокету. Доступный snapshotter зависит от ядра и окружения: на подходящем современном ядре возможен OverlayFS, в других случаях применяется fuse-overlayfs, а наиболее совместимым, но менее производительным вариантом служит native snapshotter. В rootless worker сеть по умолчанию имеет ограничения и использует режим host.
Если rootless BuildKit запускается внутри контейнера, возникает вложенная контейнеризация. Для неё могут потребоваться ослабленные профили seccomp и AppArmor, доступ к системным путям или привилегированный контейнер. Такие параметры нельзя считать эквивалентом обычного rootless-процесса на VDS: внешний контейнерный runtime всё равно определяет доступные системные вызовы и mount-операции.
Rootless Buildah
Buildah запускается непосредственно от обычного пользователя и для непривилегированного режима выбирает соответствующую изоляцию. Ему нужны диапазоны subordinate UID и GID, обычно заданные в /etc/subuid и /etc/subgid, а также утилиты newuidmap и newgidmap. Это позволяет отображать пользователей внутри сборочного контейнера на непривилегированные идентификаторы хоста.
Для хранения слоёв Buildah использует ту же инфраструктуру, что и Podman. Rootless-хранилище обычно находится в домашнем каталоге пользователя. На системах, где непривилегированный OverlayFS недоступен или нежелателен, применяется fuse-overlayfs. Хранилище лучше размещать на локальной файловой системе VDS: NFS и некоторые распределённые файловые системы плохо совместимы с user namespaces и rootless overlay.
Rootless внутри CI-контейнера
Главная практическая ошибка — проверить BuildKit или Buildah на обычном VDS, а затем ожидать того же поведения внутри изолированного CI-задания. Во вложенном окружении могут быть запрещены unshare, mount-системные вызовы и доступ к /dev/fuse. Даже при наличии команд в образе сборщика операции завершатся ошибками operation not permitted или permission denied.
Перед внедрением нужно выяснить, какие возможности предоставляет среда выполнения задания:
- разрешены ли непривилегированные user namespaces;
- доступны ли
newuidmapиnewgidmap; - передаётся ли устройство
/dev/fuse; - можно ли использовать native OverlayFS внутри user namespace;
- сохраняется ли каталог с кэшем после завершения задания;
- какие seccomp-, AppArmor- и SELinux-ограничения действуют снаружи.
Если среда требует --privileged только ради запуска сборщика, преимущество rootless-модели частично теряется. Сначала стоит проверить пользовательские пространства имён и FUSE, а не отключать защитные механизмы целиком.
Dockerfile и Containerfile
Containerfile и Dockerfile — прежде всего разные имена файла. Buildah и podman build принимают оба варианта и используют общий базовый синтаксис. BuildKit ориентирован на Dockerfile frontend; альтернативное имя можно передать параметром файла.
Для проекта, который должен одинаково собираться обоими инструментами, лучше придерживаться общего подмножества инструкций:
FROM,ARG,ENVиWORKDIR;COPYиADDбез экспериментальных параметров;RUNс обычным shell- или exec-вызовом;- multi-stage-сборка через
FROM ... ASиCOPY --from; USER,ENTRYPOINT,CMDи стандартные метаданные образа.
BuildKit обычно раньше получает новые возможности Dockerfile frontend. Директива # syntax=... позволяет явно выбрать версию frontend, а RUN --mount поддерживает cache-, secret-, SSH-, bind- и tmpfs-монтирования. Кэш монтирования существует отдельно от слоя образа и может ускорять пакетные менеджеры и компиляторы. Секреты и SSH-сокеты передаются только на время конкретной операции и не должны попадать в итоговый слой.
Buildah тоже поддерживает многие современные конструкции Containerfile, но полное совпадение поведения всех расширений нельзя предполагать. Если переносимость обязательна, файл нужно действительно собирать обоими инструментами. Особенно важно тестировать параметры RUN --mount, сетевые режимы, права на bind-mount, ownership после COPY и экспериментальные инструкции frontend.
Сравнение кэша
Кэш BuildKit
BuildKit строит content-addressed-граф операций. Изменение файла влияет только на операции, которые действительно зависят от его содержимого, а независимые ветви multi-stage-сборки могут выполняться параллельно. Неиспользуемые стадии не обязательно исполняются целиком.
Кэш может оставаться внутри builder либо экспортироваться. Практически важны следующие варианты:
- internal — локальное состояние постоянного builder;
- local — отдельный каталог, который можно сохранять как артефакт или подключать к следующей сборке;
- inline — метаданные кэша, связанные с публикуемым образом;
- registry — отдельный удалённый кэш;
- другие backend-механизмы, доступность которых зависит от клиента и конфигурации BuildKit.
На постоянном VDS внутренний кэш обычно даёт наиболее простую схему: достаточно не удалять состояние buildkitd и настроить контроль занимаемого места. В эфемерном CI локальный кэш полезен только тогда, когда его каталог действительно восстанавливается перед следующим заданием. Иначе каждая сборка начинается с нуля.
Кэш RUN --mount=type=cache следует считать ускорителем, а не обязательной частью результата. Сборка должна оставаться корректной при пустом кэше, параллельном доступе и очистке данных сборщиком. Для зависимостей Composer и npm применяйте cache mounts осознанно.
Кэш Buildah
Buildah сохраняет образы и слои в containers/storage. Для повторного использования слоёв при сборке по Containerfile применяется параметр --layers. Если rootless-пользователь на VDS сохраняет свой каталог хранилища, повторные сборки могут использовать локальные результаты.
Buildah также поддерживает --cache-from и --cache-to, но их модель отличается от BuildKit: распределённый кэш основан на промежуточных образах, а параметры учитываются только вместе с --layers. Поэтому нельзя механически перенести флаги из команды Buildx и ожидать идентичного формата, объёма данных и поведения cache hit.
Для параллельного выполнения независимых стадий Buildah предлагает --jobs. На небольшом VDS значение нужно ограничивать: слишком много одновременных компиляторов или пакетных менеджеров быстро исчерпают память и создадут конкуренцию за диск. Максимальный параллелизм не всегда означает минимальное время сборки.
Что эффективнее на VDS
При большом количестве частых Dockerfile-сборок BuildKit обычно удобнее благодаря точной модели зависимостей, cache mounts и развитым экспортерам. Но преимущество исчезнет, если builder каждый раз создаётся заново, а его состояние удаляется.
Buildah практичен, когда уже используется постоянное rootless-хранилище Podman. В этом случае не нужно содержать отдельный сервис BuildKit и дублировать набор образов. Цена такого удобства — более тесная связь с containers/storage и иная модель удалённого кэша.

Multi-platform-сборка
Оба инструмента умеют собирать образы для нескольких архитектур, но само указание целевых платформ не устраняет необходимость исполнять команды RUN. Возможны три подхода:
- Нативные узлы. Каждая архитектура собирается на соответствующей машине. Обычно это самый надёжный вариант.
- Эмуляция. На хосте настроены binfmt_misc и QEMU для запуска бинарных файлов другой архитектуры. Это проще организационно, но может быть существенно медленнее.
- Кросс-компиляция. Стадия сборки выполняется на архитектуре builder, а приложение компилируется под целевую платформу. Подход особенно эффективен для языков с полноценной поддержкой cross-compilation.
Multi-platform в BuildKit
Через Buildx список платформ передаётся параметром --platform:
docker buildx build --platform linux/amd64,linux/arm64 --output type=oci,dest=example-app.oci.tar .
BuildKit предоставляет Dockerfile автоматические аргументы BUILDPLATFORM, TARGETPLATFORM, TARGETOS, TARGETARCH и TARGETVARIANT. Они позволяют отделить архитектуру стадии компилятора от архитектуры результата:
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY . .
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/app ./cmd/app
FROM alpine
COPY --from=build /out/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
Такой файл не запускает целевой бинарный файл в стадии сборки и может обойтись без эмуляции, если компилятор поддерживает нужную комбинацию ОС и архитектуры.
Multi-platform в Buildah
Buildah принимает одну или несколько платформ и добавляет результаты в manifest list или OCI image index:
buildah build --layers --platform linux/amd64,linux/arm64 --manifest example-app:multi .
При наличии RUN для ненативной архитектуры на VDS потребуется настроенная эмуляция. По возможности лучше использовать нативную архитектуру. Локальный manifest можно затем просматривать и обрабатывать командами Buildah или Podman.
Для multi-platform-сборки VDS должен иметь достаточный запас памяти и диска: каждая платформа создаёт собственные конфигурации и слои, а параллельное исполнение умножает пиковую нагрузку. На машине с ограниченными ресурсами разумнее собирать платформы последовательно.
Экспорт OCI-образов
BuildKit
BuildKit явно разделяет результат сборки и способ его экспорта. Без --output при работе через buildctl результат может остаться только во внутреннем хранилище builder. OCI-архив создаётся exporter-ом oci:
buildctl build --frontend dockerfile.v0 --local context=. --local dockerfile=. --output type=oci,dest=example-app.oci.tar
Для загрузки результата в классическое локальное хранилище Docker удобнее exporter docker или флаг --load в Buildx. OCI exporter предпочтителен, когда нужен переносимый OCI layout, multi-platform-индекс или архив для последующей обработки другим OCI-инструментом.
Buildah
Buildah по умолчанию ориентирован на OCI-формат метаданных образа, но при необходимости формат можно переключить на Docker. После сборки образ находится в локальном Containers Storage. Его можно сохранить в OCI archive через транспорт oci-archive:
buildah build --layers -t example-app:test .
buildah push example-app:test oci-archive:./example-app.oci.tar
Если образ уже доступен Podman, эквивалентный архив можно создать командой:
podman save --format oci-archive --output example-app.oci.tar example-app:test
Не следует путать сохранение образа и экспорт файловой системы контейнера. OCI archive сохраняет конфигурацию, слои и историю образа, тогда как container export обычно создаёт плоский архив файловой системы и не подходит для переноса полноценного образа.
Интеграция с Docker и Podman
BuildKit и Docker
BuildKit — естественный выбор для Docker CLI. Buildx предоставляет управление builder-экземплярами, multi-platform, экспортерами и внешним кэшем. Для обычной локальной сборки достаточно:
docker buildx build --load -t example-app:test .
Здесь --load помещает одноплатформенный результат в локальное хранилище Docker. При отдельном exporter-е или публикации результат может не появиться среди локальных образов — это нормальное следствие выбранного способа вывода, а не ошибка сборки.
Buildah и Podman
Buildah и Podman используют общий технологический стек и могут работать с одним локальным хранилищем. Поэтому типичный Podman-проект может выбирать между двумя интерфейсами:
podman build --layers -t example-app:test .
buildah build --layers -t example-app:test .
podman build удобен для команд, которым нужен знакомый интерфейс одного контейнерного CLI. Отдельный Buildah даёт больше специализированных операций над процессом создания образа.
Buildah и Docker
Buildah не записывает результат непосредственно во внутреннее хранилище Docker Engine как часть обычной сборки. Наиболее предсказуемый способ интеграции — создать Docker-совместимый архив и загрузить его:
buildah build --format docker -t example-app:test .
buildah push example-app:test docker-archive:./example-app.docker.tar:example-app:test
docker load --input example-app.docker.tar
Это добавляет этап копирования и требует свободного места для архива. Если Docker — основной runtime и такой обмен выполняется постоянно, BuildKit обычно проще.
BuildKit и Podman
BuildKit можно запускать отдельно от Docker и передавать OCI-результат Podman:
buildctl build --frontend dockerfile.v0 --local context=. --local dockerfile=. --output type=oci,dest=- | podman load
Схема полезна, если нужен именно кэш и frontend BuildKit, а запускать образы планируется через Podman. Но она сложнее нативной связки Buildah и Podman: появляется отдельный builder, его сокет и собственное хранилище кэша.
Что требуется от Linux VDS
Сборка образов требует административно контролируемого Linux-окружения. На обычном виртуальном хостинге установить BuildKit или Buildah, настроить user namespaces, FUSE и контейнерное хранилище, как правило, нельзя. Для такой задачи нужен VDS, выделенный сервер или предоставленная владельцем платформы сборочная среда.
Перед выбором инструмента стоит проверить следующие характеристики VDS:
- Ядро Linux. Оно должно поддерживать user, mount и другие необходимые namespaces. Современное ядро упрощает rootless OverlayFS.
- Subordinate IDs. Для rootless Buildah и Podman пользователь должен иметь уникальные диапазоны в
/etc/subuidи/etc/subgid. - Вспомогательные утилиты. Нужны
newuidmapиnewgidmap; для некоторых конфигураций — RootlessKit,fuse-overlayfsи средство пользовательской сети. - Локальная файловая система. Каталоги кэша и слоёв лучше хранить на локальном ext4, XFS или другом поддерживаемом хранилище, а не в NFS-home.
- Свободные inode и место. Контейнерные слои состоят из большого числа файлов, поэтому одного показателя свободных гигабайтов недостаточно.
- Оперативная память. Пиковое потребление определяется не сборщиком как таковым, а компиляторами, пакетными менеджерами и числом параллельных стадий.
- Эмуляция архитектур. Для ненативных
RUNпотребуются настроенные binfmt_misc и соответствующий эмулятор.
Базовую диагностику можно выполнить без изменения системы:
uname -r
unshare -Ur true
grep "^$(id -un):" /etc/subuid /etc/subgid
command -v newuidmap newgidmap
command -v fuse-overlayfs
findmnt -T "$HOME"
df -h "$HOME"
df -i "$HOME"
Отсутствие вывода grep означает, что subordinate IDs для пользователя не настроены. Изменять их должен администратор VDS: диапазоны разных пользователей не должны пересекаться. После изменения уже работающему rootless-окружению может потребоваться миграция или пересоздание пользовательского состояния.
Параметр kernel.unprivileged_userns_clone встречается не во всех дистрибутивах, поэтому проверку конкретного sysctl нельзя считать универсальной. Практический вызов unshare -Ur true надёжнее показывает, разрешено ли создание непривилегированного user namespace в текущем окружении.
Безопасность сборочного процесса
Rootless снижает полномочия сборщика, но не делает недоверенный Dockerfile безопасным. Инструкция RUN выполняет произвольный код, способный читать доступный контекст, обращаться к сети и расходовать ресурсы пользователя. Не следует передавать в контекст домашний каталог, SSH-ключи, файлы конфигурации CI и другие данные, не нужные сборке.
Секреты нельзя помещать в ARG, копировать в слой с последующим удалением или записывать в команды, сохраняемые в истории. Следует применять secret mounts, SSH mounts либо соответствующие механизмы Buildah и проверять, что выбранная конструкция поддерживается конкретным сборщиком.
Сокет удалённого buildkitd тоже является чувствительным ресурсом. Пользователь с доступом к builder может запускать сборочные операции и расходовать его CPU, память и диск. Сокет должен быть доступен только доверенным учётным записям, а постоянный builder нуждается в лимитах и очистке кэша.
Практические сценарии выбора
Docker-проект с частыми сборками
Выбирайте BuildKit через Buildx. Он даёт минимальное число промежуточных преобразований, богатые возможности Dockerfile и гибкий кэш. На VDS имеет смысл создать постоянный builder и выделить отдельный диск или каталог под его состояние.
Rootless Podman на одном VDS
Выбирайте Buildah или podman build. Сборка и запуск используют совместимое локальное хранилище, не нужен дополнительный демон, а управление правами сосредоточено в настройках одного пользователя.
Эфемерные задания без сохраняемого диска
Оценивайте не скорость первой сборки, а возможность восстановить кэш. BuildKit обычно предлагает более гибкие cache exporters. Buildah подходит, если среда позволяет сохранить Containers Storage или использовать распределённый кэш с обязательным --layers.
Один Containerfile для Docker и Podman
Используйте переносимое подмножество синтаксиса и запускайте две тестовые сборки. Имя файла можно выбрать любое и передавать явно через -f. Если используются расширенные mounts, секреты или platform-аргументы, совместимость нужно проверять отдельно.
Multi-platform-образ на небольшом VDS
BuildKit удобнее как единый интерфейс multi-platform-сборки, особенно при кросс-компиляции. Buildah остаётся рабочим вариантом в Podman-окружении. Независимо от инструмента лучше ограничить параллелизм и избегать QEMU там, где приложение можно кросс-компилировать.
Изолированная сборка без постоянного сервиса
Buildah проще операционно: процесс завершился — отдельного демона не осталось. BuildKit тоже поддерживает временные схемы запуска, но им требуются запуск builder и передача результата, что добавляет компоненты.
Итоговое сравнение
- Архитектура: BuildKit использует builder-сервис и граф LLB; Buildah работает как daemonless CLI поверх Containers Storage.
- Rootless: оба поддерживают непривилегированную сборку, но BuildKit обычно требует RootlessKit, а Buildah опирается на user namespaces и конфигурацию стека Containers.
- Dockerfile: BuildKit даёт наиболее полную интеграцию с современным Dockerfile frontend; Buildah принимает Dockerfile и Containerfile, но расширения нужно тестировать.
- Кэш: BuildKit отличается развитой content-addressed-моделью и экспортерами; Buildah использует локальные слои и собственную модель распределённых промежуточных образов.
- Multi-platform: оба инструмента поддерживают несколько платформ и manifest/index; для ненативных
RUNобоим нужна эмуляция либо нативный узел. - OCI: BuildKit экспортирует OCI layout напрямую; Buildah естественно работает с OCI-метаданными и транспортами OCI.
- Интеграция: BuildKit предпочтителен для Docker, Buildah — для Podman.
Универсального победителя нет. Если команда уже стандартизировала Docker CLI, использует сложный Dockerfile и хочет переносимый кэш, рациональнее выбрать BuildKit. Если на Linux VDS развёрнут rootless Podman, важна daemonless-модель и непосредственный доступ к локальным образам, Buildah уменьшит число компонентов. Смешанная схема тоже возможна, но её следует выбирать ради конкретной функции — например, BuildKit-кэша при последующем запуске в Podman, — а не ради абстрактной универсальности.


