Jump to…
snowfix: 500 error because of database is patchedyuzpxzopsouq1mo
Matt W1# Dogfood — multi-stage build.
Matt W2#
Matt W3# One builder stage produces every binary; each runtime target copies just the
Matt W4# one it needs, so the three images share all their build layers (spec §10).
Matt W5
Matt W6# ─── editor bundle ───────────────────────────────────────────────────────────
Matt W7# CodeMirror is ESM and has to be bundled. Node lives in this stage only — the
Matt W8# runtime images below copy a single .js file and never see a package manager,
Matt W9# which is the objection spec §1 raises against a Node toolchain, not to
Matt W10# bundling as such.
Matt W11FROM node:22-alpine AS editor
Matt W12WORKDIR /editor
Matt W13COPY crates/df-web/assets/editor/package.json ./
Matt W14RUN npm install --no-audit --no-fund --silent
Matt W15COPY crates/df-web/assets/editor/editor.js ./
Matt W16RUN npx esbuild editor.js --bundle --minify --format=iife --target=es2020 \
Matt W17 --outfile=/editor/editor.min.js \
Matt W18 && ls -la /editor/editor.min.js
Matt W19
Matt W20# ─── planner: cache dependency compilation ───────────────────────────────────
Matt W21FROM rust:1-slim-bookworm AS chef
Matt W22WORKDIR /build
Matt W23# `build-essential` is needed for the tree-sitter grammars, which are C
Matt W24# libraries compiled into df-render (spec §8).
Matt W25RUN apt-get update \
Matt W26 && apt-get install -y --no-install-recommends pkg-config libssl-dev build-essential \
Matt W27 && rm -rf /var/lib/apt/lists/*
Matt W28RUN cargo install cargo-chef --locked
Matt W29
Matt W30FROM chef AS planner
Matt W31COPY Cargo.toml Cargo.lock* ./
Matt W32COPY crates ./crates
Matt W33COPY migrations ./migrations
Matt W34RUN cargo chef prepare --recipe-path recipe.json
Matt W35
Matt W36# ─── builder ─────────────────────────────────────────────────────────────────
Matt W37FROM chef AS builder
Matt W38COPY --from=planner /build/recipe.json recipe.json
Matt W39# Dependencies are built once and cached; only workspace source changes below
Matt W40# this line invalidate the layer.
Matt W41RUN cargo chef cook --release --recipe-path recipe.json
Matt W42
Matt W43COPY Cargo.toml Cargo.lock* ./
Matt W44COPY crates ./crates
Matt W45# `sqlx::migrate!` embeds these at compile time, so they must be present.
Matt W46COPY migrations ./migrations
Matt W47# …and so is the editor bundle, by `include_str!`. It has to land before the
Matt W48# build, not after.
Matt W49COPY --from=editor /editor/editor.min.js ./crates/df-web/assets/editor.min.js
Matt W50RUN cargo build --release --bin dogfood-web --bin dogfood-worker --bin dogfood-admin --bin dogfood-hook --bin dogfood-ssh
Matt W51
Matt W52# ─── runtime base ────────────────────────────────────────────────────────────
Matt W53FROM debian:bookworm-slim AS runtime-base
Matt W54RUN apt-get update \
Matt W55 && apt-get install -y --no-install-recommends ca-certificates git wget \
Matt W56 && rm -rf /var/lib/apt/lists/* \
Matt W57 && useradd --system --create-home --uid 10001 dogfood \
Matt W58 && mkdir -p /srv/repos \
Matt W59 && chown dogfood:dogfood /srv/repos
Matt W60USER dogfood
Matt W61WORKDIR /app
Matt W62
Matt W63# ─── web ─────────────────────────────────────────────────────────────────────
Matt W64FROM runtime-base AS web
Matt W65COPY --from=builder /build/target/release/dogfood-web /usr/local/bin/dogfood-web
Matt W66# Installed into each repository's hooks/ directory at creation.
Matt W67COPY --from=builder /build/target/release/dogfood-hook /usr/local/bin/dogfood-hook
Matt W68EXPOSE 8080
Matt W69# /readyz, not /healthz: /healthz is an unconditional 200, so it reported this
Matt W70# container healthy through the two hours of 2026-08-03 in which every request
Matt W71# 500ed on an exhausted connection pool. Docker does not act on this status by
Matt W72# itself — compose restarts on exit, not on unhealthy — so this buys visibility,
Matt W73# not recovery. The 5s timeout is deliberately below the pool's 10s
Matt W74# acquire_timeout, so a pool that can no longer hand out connections fails the
Matt W75# check instead of slowly answering it.
Matt W76HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
Matt W77 CMD wget -qO- http://127.0.0.1:8080/readyz >/dev/null || exit 1
Matt W78ENTRYPOINT ["/usr/local/bin/dogfood-web"]
Matt W79
Matt W80# ─── worker ──────────────────────────────────────────────────────────────────
Matt W81FROM runtime-base AS worker
Matt W82COPY --from=builder /build/target/release/dogfood-worker /usr/local/bin/dogfood-worker
Matt W83# The admin CLI ships in the worker image so operational commands run where the
Matt W84# repository volume is mounted.
Matt W85COPY --from=builder /build/target/release/dogfood-admin /usr/local/bin/dogfood-admin
Matt W86COPY --from=builder /build/target/release/dogfood-hook /usr/local/bin/dogfood-hook
Matt W87ENTRYPOINT ["/usr/local/bin/dogfood-worker"]
Matt W88
Matt W89# ─── ssh ─────────────────────────────────────────────────────────────────────
Matt W90# Git over SSH (spec §6, §9). Public-key auth only, and `dogfood-ssh` dispatches
Matt W91# nothing but `git-upload-pack` and `git-receive-pack` — no shell, no PTY, no
Matt W92# forwarding — so the image needs `git` and nothing else.
Matt W93FROM runtime-base AS ssh
Matt W94# The host-key directory must exist in the *image*, owned by the runtime user.
Matt W95# Docker seeds a named volume from the image path it is mounted at, including
Matt W96# ownership — an empty volume on a path that does not exist comes up owned by
Matt W97# root, and the process (uid 10001) then cannot write its host key.
Matt W98USER root
Matt W99RUN mkdir -p /etc/dogfood && chown dogfood:dogfood /etc/dogfood
Matt W100USER dogfood
Matt W101COPY --from=builder /build/target/release/dogfood-ssh /usr/local/bin/dogfood-ssh
Matt W102# Installed into each repository's hooks/ directory; the SSH path receives
Matt W103# pushes and must validate them the same way HTTPS does.
Matt W104COPY --from=builder /build/target/release/dogfood-hook /usr/local/bin/dogfood-hook
Matt W105# The host sshd owns :22 on this deployment, so the container listens on 2222
Matt W106# and is published there too — clone URLs carry the explicit port.
Matt W107EXPOSE 2222
Matt W108ENTRYPOINT ["/usr/local/bin/dogfood-ssh"]

108 lines · Shell