# ============================================================================= # Zonemaster Backend — multi-stage build from source # Chain: LDNS → Engine → CLI → Backend # ============================================================================= # ── Stage 1: Build LDNS ────────────────────────────────────────────────────── FROM alpine:3.22 AS ldns-build RUN apk add --no-cache \ autoconf \ automake \ build-base \ libidn2-dev \ libtool \ make \ openssl-dev \ perl-app-cpanminus \ perl-dev \ perl-devel-checklib \ perl-extutils-depends \ perl-extutils-pkgconfig \ perl-lwp-protocol-https \ perl-mime-base32 \ perl-module-install \ perl-test-differences \ perl-test-fatal \ perl-test-nowarnings \ && cpanm --notest --no-wget --from=https://cpan.metacpan.org/ \ Module::Install::XSUtil COPY zonemaster-ldns/ /src/ldns/ RUN cpanm --notest --no-wget /src/ldns/ # ── Stage 2: LDNS runtime base ──────────────────────────────────────────────── FROM alpine:3.22 AS ldns COPY --from=ldns-build \ /usr/local/lib/perl5/site_perl/auto/Zonemaster \ /usr/local/lib/perl5/site_perl/auto/Zonemaster COPY --from=ldns-build \ /usr/local/lib/perl5/site_perl/Zonemaster \ /usr/local/lib/perl5/site_perl/Zonemaster RUN apk add --no-cache libidn2 perl # ── Stage 3: Build Engine ───────────────────────────────────────────────────── FROM ldns AS engine-build RUN apk add --no-cache \ gcc \ gettext \ make \ musl-dev \ perl-dev \ perl-app-cpanminus \ perl-mailtools \ perl-module-build-tiny \ perl-class-accessor \ perl-clone \ perl-file-sharedir \ perl-file-slurp \ perl-io-socket-inet6 \ perl-list-moreutils \ perl-locale-msgfmt \ perl-log-any \ perl-lwp-protocol-https \ perl-mail-spf \ perl-module-install \ perl-pod-coverage \ perl-readonly \ perl-sub-override \ perl-test-differences \ perl-test-exception \ perl-test-fatal \ perl-test-nowarnings \ perl-test-pod \ perl-text-csv \ perl-yaml \ perl-yaml-libyaml \ && cpanm --no-wget --from=https://cpan.metacpan.org/ \ Email::Valid \ List::Compare \ Locale::PO \ Locale::TextDomain \ Module::Find \ Net::DNS \ Net::IP::XS COPY zonemaster-engine/ /src/engine/ RUN cpanm --notest --no-wget /src/engine/ # ── Stage 4: Engine runtime base ───────────────────────────────────────────── FROM ldns AS engine COPY --from=engine-build /usr/local/lib/perl5/site_perl /usr/local/lib/perl5/site_perl COPY --from=engine-build /usr/local/share/perl5/site_perl /usr/local/share/perl5/site_perl RUN apk add --no-cache \ musl-locales \ perl-class-accessor \ perl-clone \ perl-file-sharedir \ perl-file-slurp \ perl-io-socket-inet6 \ perl-list-moreutils \ perl-locale-msgfmt \ perl-log-any \ perl-mail-spf \ perl-mailtools \ perl-module-install \ perl-net-ip \ perl-readonly \ perl-text-csv \ perl-try-tiny \ perl-yaml-libyaml # ── Stage 5: Build CLI ──────────────────────────────────────────────────────── FROM engine AS cli-build RUN apk add --no-cache \ gettext \ make \ perl-app-cpanminus \ perl-json-xs \ perl-lwp-protocol-https \ perl-mojolicious \ perl-test-deep \ perl-test-differences \ perl-try-tiny \ && cpanm --notest --no-wget --from=https://cpan.metacpan.org/ \ JSON::Validator COPY zonemaster-cli/ /src/cli/ RUN cpanm --notest --no-wget /src/cli/ # ── Stage 6: CLI runtime base ───────────────────────────────────────────────── FROM engine AS cli RUN apk add --no-cache perl-json-xs perl-try-tiny COPY --from=cli-build /usr/local/bin/zonemaster-cli /usr/local/bin/zonemaster-cli COPY --from=cli-build /usr/local/lib/perl5/site_perl /usr/local/lib/perl5/site_perl COPY --from=cli-build /usr/local/share/perl5/site_perl /usr/local/share/perl5/site_perl # ── Stage 7: Build Backend ──────────────────────────────────────────────────── FROM cli AS backend-build RUN apk add --no-cache \ gettext \ make \ curl \ gcc \ perl-dev \ musl-dev \ perl-app-cpanminus \ jq \ perl-class-method-modifiers \ perl-config-inifiles \ perl-dbd-sqlite \ perl-dbi \ perl-file-share \ perl-file-slurp \ perl-html-parser \ perl-http-parser-xs \ perl-mojolicious \ perl-io-stringy \ perl-libwww \ perl-log-any \ perl-log-dispatch \ perl-moose \ perl-parallel-forkmanager \ perl-plack \ perl-role-tiny \ perl-test-nowarnings \ perl-test-differences \ perl-test-exception \ perl-try-tiny \ perl-doc \ && cpanm --notest --no-wget --from=https://cpan.metacpan.org/ \ Daemon::Control \ JSON::RPC \ JSON::Validator \ Log::Any::Adapter::Dispatch \ Net::Statsd \ Plack::Middleware::ReverseProxy \ Router::Simple::Declare COPY zonemaster-backend/ /src/backend/ RUN cpanm --notest --no-wget /src/backend/ # ── Stage 8: Backend runtime (final image) ──────────────────────────────────── FROM cli AS backend RUN apk add --no-cache \ jq \ perl-config-inifiles \ perl-mojolicious \ perl-moose \ perl-dbi \ perl-dbd-sqlite \ perl-plack \ perl-parallel-forkmanager COPY --from=backend-build /usr/local/share/perl5 /usr/local/share/perl5 COPY --from=backend-build /usr/local/bin/ /usr/local/bin/ COPY --from=backend-build /usr/lib/perl5 /usr/lib/perl5 RUN addgroup -S zonemaster \ && adduser -S zonemaster -G zonemaster RUN SHARE=$(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")') \ && install -v -m 755 -d /etc/zonemaster \ && install -v -m 775 -g zonemaster -d /var/log/zonemaster \ && install -v -m 640 -g zonemaster "$SHARE/backend_config.ini" /etc/zonemaster/ RUN install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster USER zonemaster RUN "$(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl" USER root # Install s6-overlay for process supervision (rpcapi + testagent) ARG S6_OVERLAY_VERSION=3.2.1.0 ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz COPY zonemaster-backend/zonemaster_launch /usr/local/bin/zonemaster_launch RUN chmod +x /usr/local/bin/zonemaster_launch # Register rpcapi service (JSON-RPC API on :5000) RUN mkdir -p /etc/s6-overlay/s6-rc.d/rpcapi \ && echo "longrun" > /etc/s6-overlay/s6-rc.d/rpcapi/type \ && printf '#!/command/with-contenv sh\nexec zonemaster_launch rpcapi\n' \ > /etc/s6-overlay/s6-rc.d/rpcapi/run \ && chmod +x /etc/s6-overlay/s6-rc.d/rpcapi/run \ && touch /etc/s6-overlay/s6-rc.d/user/contents.d/rpcapi # Register testagent service (background test runner) RUN mkdir -p /etc/s6-overlay/s6-rc.d/testagent \ && echo "longrun" > /etc/s6-overlay/s6-rc.d/testagent/type \ && printf '#!/command/with-contenv sh\nexec zonemaster_launch testagent\n' \ > /etc/s6-overlay/s6-rc.d/testagent/run \ && chmod +x /etc/s6-overlay/s6-rc.d/testagent/run \ && touch /etc/s6-overlay/s6-rc.d/user/contents.d/testagent EXPOSE 5000 ENTRYPOINT ["/init"]