Build:
- 1
- 0
2026-07-09 16:05.21: New job: ocaml-docs-ci {"image":"ocurrent/ocaml-docs-ci@sha256:c185433c435fd21926e9fbd04cf102e7bda12c6246d9fc2c8a28981c34e50b05"}
2026-07-09 16:05.21: Checking out commit 07af7f37. To reproduce:
git clone --recursive "https://github.com/ocurrent/ocaml-docs-ci.git" -b "dill" && cd "ocaml-docs-ci" && git reset --hard 07af7f37
2026-07-09 16:05.21: Exec: "cp" "-a" "--" "/var/lib/ocurrent/var/git/ocaml-docs-ci.git-f2841974542651d306d2cfeb807c13b7948df1dcad0f20da42c0688683c00f56/.git"
"/tmp/git-checkout2785980f"
2026-07-09 16:05.21: Exec: "git" "-C" "/tmp/git-checkout2785980f" "submodule"
"deinit" "--force" "--all"
2026-07-09 16:05.21: Exec: "git" "-C" "/tmp/git-checkout2785980f" "reset"
"--hard" "-q" "07af7f37465ae598d79f014e8f42a3b35cca2ce5"
2026-07-09 16:05.21: Exec: "git" "-c" "protocol.file.allow=always" "-C"
"/tmp/git-checkout2785980f" "submodule" "update"
"--recursive" "--init" "--no-fetch"
2026-07-09 16:05.21: docker-compose.yml
# ocaml-docs-ci, post-day11.
#
# Two services:
# - daemon — runs the ocaml-docs-ci binary. Drives runc directly
# to build each opam package in a sandboxed container, so it
# needs --privileged plus access to the host's runc (the
# debian-12 runc shipped in the image is enough on most kernels;
# swap to a bind-mount of /usr/bin/runc if the host's is newer).
# - web — Caddy, serving rendered HTML from the day11 cache
# directly and reverse-proxying everything else (dashboard, job
# logs, snapshot pages) to the daemon at :8080.
#
# The daemon resolves everything under /home/app (the image's app-user
# HOME). The whole [.day11] tree lives on a single named volume mounted
# at /home/app/.day11. The two profiles it runs are baked into the image
# at /home/app/.day11/profiles by the Dockerfile
# Everything under .day11 — cache,
# snapshots, overlays, and scratch (TMPDIR=/home/app/.day11/cache/tmp) —
# shares one volume/filesystem, so the daemon's hardlinks (cp --link
# of dep layers from cache into scratch on Stack.merge) and its cross-dir
# rename()s all stay within a single mount and just work.
services:
daemon:
# ocurrent-deployer pins this to a name@sha256 digest before deploying.
image: ocurrent/ocaml-docs-ci@sha256:c185433c435fd21926e9fbd04cf102e7bda12c6246d9fc2c8a28981c34e50b05
restart: unless-stopped
privileged: true
# The daemon builds (and imports) its base image via the host
# Docker daemon — [docker build] / [docker create] over the mounted
# socket below. The socket is group-owned by [docker]; since the
# daemon runs as the non-root app user, add that group so it can
# talk to the socket. This must match the docker group's gid *on the
# host*: dill's docker.io install put it at 107 (999 was already taken
# by systemd-journal). Update this if the host's docker gid differs.
group_add:
- "107"
# The default soft nofile limit (1024) is far too low for hundreds
# of concurrent builds — each holds open logs, overlay dirs, and
# pipes to sudo/runc, and exhaustion (EMFILE) cascades into mass
# build failures.
ulimits:
nofile:
soft: 1048576
hard: 1048576
init: false # dumb-init already in the image
ports:
- "8080:8080"
command:
# The image entrypoint is generic (it only preps TMPDIR), so the
# command names the binary. Run one-off CLI tools in the same
# environment with e.g.:
# docker compose run --rm daemon day11 batch --profile=full ...
- ocaml-docs-ci
# Only the lightweight 'quick' profile runs (Jane Street packages,
# latest version only). The heavier ocaml-odoc-master and oxcaml
# profiles are still baked into the image (docker/profiles/) but
# disabled here — add them back to this list to re-enable.
# 'eliom-fix' builds the latest version of every package against
# odoc + odoc-driver from jonludlam/odoc#eliom-fix (see the
# --github-pin-overlay below). 'eliom-baseline' is its control: an
# identical profile (same compiler 5.4.1, same dune.3.23.1 pin, same
# opam-repo) but WITHOUT the eliom-fix overlay, so it uses mainline
# odoc/driver. Diffing eliom-fix vs eliom-baseline isolates the
# fork's odoc rendering changes from compiler/solve differences.
- --profiles=quick,full,eliom-fix,eliom-baseline,quick-ubuntu
# Republish odoc/odoc-driver/odoc-md/odoc-parser/sherlodoc from the
# eliom-fix branch of the jonludlam/odoc fork as an opam overlay
# under .day11/overlays/eliom-fix/repo, which the eliom-fix profile
# references in its opam_repositories. Tracks the branch HEAD on the
# hourly schedule and re-plans when it moves.
- --github-pin-overlay=https://github.com/jonludlam/odoc#eliom-fix=overlays/eliom-fix
# All opam-repository mirrors live inside the day11 volume, under
# .day11/repo/. The clone-target PATHs below are relative, so
# ocaml-docs-ci resolves them against the .day11 root (the parent
# of --profile-dir, i.e. /home/app/.day11) — exactly as it resolves
# the matching [opam_repositories] entries in the profiles, so the
# two line up and live repo-polling hooks up. git clone creates the
# missing [repo/...] parents on first run.
- --remote=https://github.com/ocaml/opam-repository.git=repo/opam-repository
- --jobs=40
- --cores-per-build=4
- --overcommit=2.5
- --port=8080
volumes:
# All of .day11 on a single named volume: cache (built layers +
# rendered HTML), snapshots, overlays, the opam-repository mirrors
# (repo/, git-cloned by --remote on first run), and scratch (TMPDIR=
# /home/app/.day11/cache/tmp). The image-baked profiles at
# /home/app/.day11/profiles are preserved — an empty named volume
# is seeded from the image's directory on first creation, so unlike
# a bind-mount it doesn't shadow them.
- day11:/home/app/.day11
# OCurrent state (sqlite + per-job logs). Mounted under
# /var/lib/ocurrent matching the image's WORKDIR.
- ocurrent-data:/var/lib/ocurrent
# Build/import the base image via the host Docker daemon.
- /var/run/docker.sock:/var/run/docker.sock
environment:
# HOME stays /home/app (set in the image); the daemon derives
# ~/.day11/{profiles,cache} from it.
- OCAMLRUNPARAM=b
web:
image: caddy:alpine
restart: unless-stopped
depends_on:
- daemon
ports:
# 80 for the ACME HTTP-01 challenge + HTTP->HTTPS redirect, 443 for
# TLS (and 443/udp for HTTP/3). Caddy auto-provisions a Let's Encrypt
# cert for the domain named in the Caddyfile; both must be reachable
# from the internet for issuance to succeed.
- "80:80"
- "443:443"
- "443:443/udp"
# The Caddyfile is shipped to the remote daemon as an inline Compose
# config (see the top-level `configs:` block below) rather than bind-
# mounting /etc/caddy. ocurrent-deployer runs `docker --context
# dill.caelum.ci.dev compose up`, so any bind source would resolve on
# the remote host's filesystem, not in the deployer's checkout — only
# an inline config ships the file's bytes to the remote daemon.
configs:
- source: caddyfile
target: /etc/caddy/Caddyfile
volumes:
# Serve rendered HTML straight from the shared day11 volume,
# mounted read-only at /srv. The per-arch cache root is a subdir
# of it (/srv/cache/debian-bookworm-x86_64); the Caddyfile prepends
# that prefix. NB: not /data — that's Caddy's own state dir.
- day11:/srv:ro
# Caddy's data (instance id, any future certs) and autosaved
# config. Named so they survive container recreation rather than
# piling up as anonymous volumes.
- caddy-data:/data
- caddy-config:/config
volumes:
day11:
caddy-data:
caddy-config:
ocurrent-data:
# Caddy front-end config, inlined so the deployment is self-contained and
# needs nothing pre-placed on the host. Compose ships this content to the
# remote daemon and mounts it at /etc/caddy/Caddyfile in the web service.
configs:
caddyfile:
content: |
# Caddy front-end for ocaml-docs-ci.
#
# Two responsibilities:
# 1. Serve rendered HTML straight from the day11 cache.
# Each profile's docs are built into a per-toolchain epoch dir and
# exposed through a [html-live] symlink that's swapped manually on
# promotion. day11 lays a profile's live docs out at
# [<cache>/html-<profile>/html-live/p/<pkg>/<ver>/doc/index.html]
# (html-live -> epoch-<hash>/html), and the daemon links to
# [/profiles/<NAME>/docs/p/<pkg>/<ver>/...].
# The .day11 volume is mounted read-only at /srv (Caddy keeps its
# own state on /data, so we can't reuse that path). Rendered HTML
# is arch-independent, so it lives under a flat per-profile dir —
# live docs are under [/srv/cache/html/<profile>/html-live/].
# The html-live symlink is *relative* (epoch-<hash>/html), so it
# resolves within /srv. Until the first promote, html-live is absent
# and these routes 404 (the rest of the site still proxies fine).
# 2. Reverse-proxy everything else to the daemon's web UI on :8080 —
# the dashboard, snapshot pages, /job/ log viewer, diff pages, etc.
#
# TLS: naming a real domain (not :80) turns on Caddy's automatic HTTPS —
# it provisions and renews a Let's Encrypt cert over ACME and redirects
# plain HTTP to HTTPS. Needs ports 80 + 443 reachable from the internet
# and a DNS record for the domain pointing here. Certs/account key persist
# on the /data volume so restarts don't re-request.
dill.caelum.ci.dev {
# gzip-compress the static .css/.js/.json odoc emits (and HTML).
encode gzip
# Allow large request bodies for anything the daemon surfaces
# (e.g. snapshot uploads via the API).
request_body {
max_size 100MB
}
# /profiles/<name>/docs/<rest> → serve from the day11 cache.
# Capture 1 is the profile, capture 2 the path within its html tree.
@docs path_regexp docs ^/profiles/([^/]+)/docs/(.*)$
handle @docs {
root * /srv/cache/html/{re.docs.1}/html-live
rewrite * /{re.docs.2}
# odoc trees carry an index.html in every directory; serve it for
# bare-directory requests. Missing files 404 (file_server default).
file_server {
index index.html
}
}
# Everything else → the daemon. Caddy forwards the Host header and
# sets the X-Forwarded-* headers automatically.
handle {
reverse_proxy daemon:8080 {
# Don't buffer responses: the /job/ log viewer streams
# (SSE-style) and must flush as data arrives. Caddy applies
# no upstream read timeout by default, so long-lived streams
# stay open (nginx needed an explicit 1d bump for this).
flush_interval -1
}
}
}
2026-07-09 16:05.21: Exec: "docker" "--context" "dill.caelum.ci.dev"
"compose" "-p" "ocaml-docs-ci" "-f" "/tmp/git-checkout2785980f/docker-compose.yml"
"pull"
Image caddy:alpine Pulling
Image ocurrent/ocaml-docs-ci@sha256:c185433c435fd21926e9fbd04cf102e7bda12c6246d9fc2c8a28981c34e50b05 Pulling
a0449c657909 Pulling fs layer 0B
e6f31ffc071e Pulling fs layer 0B
ee31d5a470f0 Pulling fs layer 0B
f8432a27d075 Pulling fs layer 0B
4f4fb700ef54 Pulling fs layer 0B
ee31d5a470f0 Downloading 7.502kB
a0449c657909 Downloading 1.049MB
e6f31ffc071e Downloading 1.049MB
ee31d5a470f0 Downloading 7.502kB
ee31d5a470f0 Download complete 0B
a0449c657909 Downloading 1.049MB
e6f31ffc071e Downloading 1.049MB
e6f31ffc071e Downloading 2.097MB
f8432a27d075 Downloading 1.049MB
a0449c657909 Downloading 3.146MB
f8432a27d075 Download complete 0B
a0449c657909 Downloading 8.389MB
e6f31ffc071e Download complete 0B
e6f31ffc071e Extracting 1B
a0449c657909 Downloading 10.49MB
4f4fb700ef54 Downloading 32B
e6f31ffc071e Extracting 1B
4f4fb700ef54 Downloading 32B
a0449c657909 Downloading 10.49MB
e6f31ffc071e Extracting 1B
a0449c657909 Downloading 15.73MB
4f4fb700ef54 Download complete 0B
e6f31ffc071e Extracting 1B
a0449c657909 Download complete 0B
e6f31ffc071e Extracting 1B
01cedcff86f8 Pulling fs layer 0B
5c61adb4cd02 Pulling fs layer 0B
12c608f306e0 Pulling fs layer 0B
a976e540c872 Pulling fs layer 0B
6d085c30ffa6 Pulling fs layer 0B
a513c5204ad8 Pulling fs layer 0B
126a2d8d8f76 Pulling fs layer 0B
2f6e1fb397d5 Pulling fs layer 0B
8054ad6ae70f Pulling fs layer 0B
7fccd04675d0 Download complete 0B
a39a20466be4 Downloading 314.5kB
e6f31ffc071e Extracting 1B
01cedcff86f8 Downloading 1.049MB
a39a20466be4 Downloading 314.5kB
e6f31ffc071e Extracting 1B
01cedcff86f8 Downloading 5.243MB
a39a20466be4 Download complete 0B
f8432a27d075 Extracting 1B
e6f31ffc071e Pull complete 0B
01cedcff86f8 Downloading 12.58MB
ee31d5a470f0 Pull complete 0B
f8432a27d075 Pull complete 0B
a0449c657909 Extracting 1B
01cedcff86f8 Downloading 18.87MB
a0449c657909 Extracting 1B
a513c5204ad8 Downloading 189B
01cedcff86f8 Downloading 19.92MB
a0449c657909 Extracting 1B
01cedcff86f8 Downloading 22.02MB
8054ad6ae70f Download complete 0B
2f6e1fb397d5 Download complete 0B
a976e540c872 Download complete 0B
126a2d8d8f76 Download complete 0B
6d085c30ffa6 Download complete 0B
a513c5204ad8 Download complete 0B
a0449c657909 Extracting 1B
01cedcff86f8 Downloading 33.55MB
a0449c657909 Extracting 1B
4f4fb700ef54 Pull complete 0B
a0449c657909 Pull complete 0B
Image caddy:alpine Pulled
01cedcff86f8 Downloading 44.04MB
01cedcff86f8 Downloading 48.5MB
5c61adb4cd02 Downloading 1.049MB
01cedcff86f8 Downloading 48.5MB
5c61adb4cd02 Downloading 3.146MB
01cedcff86f8 Download complete 0B
5c61adb4cd02 Downloading 7.34MB
12c608f306e0 Downloading 2.097MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 9.437MB
12c608f306e0 Downloading 4.194MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 12.58MB
12c608f306e0 Downloading 6.291MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 16.78MB
12c608f306e0 Downloading 10.49MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 19.92MB
12c608f306e0 Downloading 13.63MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 22.02MB
12c608f306e0 Downloading 15.73MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 25.17MB
12c608f306e0 Downloading 20.97MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 28.31MB
12c608f306e0 Downloading 24.12MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 31.46MB
12c608f306e0 Downloading 27.26MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 34.6MB
12c608f306e0 Downloading 32.51MB
01cedcff86f8 Extracting 1B
5c61adb4cd02 Downloading 37.75MB
12c608f306e0 Downloading 35.65MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 39.85MB
12c608f306e0 Downloading 39.85MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 41.94MB
12c608f306e0 Downloading 45.09MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 46.14MB
12c608f306e0 Downloading 45.64MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 47.19MB
12c608f306e0 Downloading 45.64MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 55.57MB
12c608f306e0 Download complete 0B
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 62.91MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 70.25MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 75.5MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 81.79MB
01cedcff86f8 Extracting 2B
5c61adb4cd02 Downloading 83.89MB
01cedcff86f8 Extracting 3B
5c61adb4cd02 Downloading 83.89MB
01cedcff86f8 Extracting 3B
5c61adb4cd02 Downloading 83.89MB
01cedcff86f8 Extracting 3B
5c61adb4cd02 Downloading 89.13MB
01cedcff86f8 Extracting 3B
5c61adb4cd02 Downloading 95.42MB
01cedcff86f8 Extracting 3B
5c61adb4cd02 Downloading 98.57MB
01cedcff86f8 Pull complete 0B
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 99.61MB
5c61adb4cd02 Downloading 100.7MB
5c61adb4cd02 Downloading 103.8MB
5c61adb4cd02 Downloading 108MB
5c61adb4cd02 Downloading 113.2MB
5c61adb4cd02 Downloading 118.5MB
5c61adb4cd02 Downloading 123.7MB
5c61adb4cd02 Downloading 129MB
5c61adb4cd02 Downloading 133.2MB
5c61adb4cd02 Downloading 133.2MB
5c61adb4cd02 Downloading 133.2MB
5c61adb4cd02 Downloading 136.3MB
5c61adb4cd02 Downloading 137.4MB
5c61adb4cd02 Downloading 138.3MB
5c61adb4cd02 Downloading 138.3MB
5c61adb4cd02 Downloading 138.3MB
5c61adb4cd02 Downloading 138.3MB
5c61adb4cd02 Downloading 138.3MB
5c61adb4cd02 Download complete 0B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 1B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 2B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 3B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 4B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 5B
5c61adb4cd02 Extracting 6B
5c61adb4cd02 Extracting 6B
2f6e1fb397d5 Pull complete 0B
a513c5204ad8 Pull complete 0B
12c608f306e0 Extracting 1B
5c61adb4cd02 Pull complete 0B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
12c608f306e0 Extracting 1B
8054ad6ae70f Pull complete 0B
a976e540c872 Pull complete 0B
126a2d8d8f76 Pull complete 0B
6d085c30ffa6 Pull complete 0B
12c608f306e0 Pull complete 0B
Image ocurrent/ocaml-docs-ci@sha256:c185433c435fd21926e9fbd04cf102e7bda12c6246d9fc2c8a28981c34e50b05 Pulled
2026-07-09 16:05.38: Exec: "docker" "--context" "dill.caelum.ci.dev"
"compose" "-p" "ocaml-docs-ci" "-f" "/tmp/git-checkout2785980f/docker-compose.yml"
"up" "-d"
Network ocaml-docs-ci_default Creating
Network ocaml-docs-ci_default Created
Volume ocaml-docs-ci_ocurrent-data Creating
Volume ocaml-docs-ci_ocurrent-data Created
Volume ocaml-docs-ci_day11 Creating
Volume ocaml-docs-ci_day11 Created
Volume ocaml-docs-ci_caddy-data Creating
Volume ocaml-docs-ci_caddy-data Created
Volume ocaml-docs-ci_caddy-config Creating
Volume ocaml-docs-ci_caddy-config Created
Container ocaml-docs-ci-daemon-1 Creating
Container ocaml-docs-ci-daemon-1 Created
Container ocaml-docs-ci-web-1 Creating
Container ocaml-docs-ci-web-1 Created
Container ocaml-docs-ci-daemon-1 Starting
Container ocaml-docs-ci-daemon-1 Started
Container ocaml-docs-ci-web-1 Starting
Container ocaml-docs-ci-web-1 Started
2026-07-09 16:08.00: Job succeeded