13 — Build, Test, and Deploy System
This is the practical "how do I actually do things" document. The Makefile is the primary interface for humans.
The All-Important Makefile
Almost everything a developer or operator does goes through targets in the root Makefile.
Key categories:
Build
make build— normal debug build ofmadmailmake build-admin-web— builds the Svelte SPA from the submodule and stampsversion.jsonmake build-with-admin-web— builds SPA + embeds it into the Rust binarymake build-release— release + admin web embeddedmake build-release-static— fully static-pie binary (no glibc dependency on target)make build-all— cross-compile release for x86_64 and aarch64
The static build is the one you usually scp to production servers.
Run / Dev
make run,make run-debugmake run-bg+make logsmake restart— stop + dev-certs + run-bg (the common edit-compile-test loop)make dev-certs— self-signed TLS for 127.0.0.1make reset-db— blow away SQLite (useful after migration edits)make install— build-with-admin-web + restart (local dev with UI)
Quality
make check,make lint(clippy -D warnings),make fmtmake test-unit,make testmake test-e2e— integration tests (builds first)make test-turn,make test-imap,make test-maintenancemake test-deltachat— full Delta Chat core E2E via incus + cmlxc (heavy; closest to real client behaviour)make test-dclogin— relay-ping against two real accounts (requires DCLOGIN1/2 in .env)
Deploy
make push,make push1,make push2— viascripts/deploy.shmake push-signed— signed upgrade path (scripts/deploy.sh HOST --signed)make sign—scripts/sign.sh(private key in../imp/private_key.hex)make log1/make log2—scripts/deploy.sh --log HOST
Remote deploys assume you have REMOTE1 / REMOTE2 (and optionally keys) in .env or context/madmail/.env.
Other
relay-ping-*targets — build the separate relay-ping tool
Embedding the Admin Web (the magic step)
Because the admin SPA is a separate SvelteKit project:
git submodule update --init external/madmail-admin-webcd external/madmail-admin-web && bun install && bun run build- From repo root:
make build-with-admin-web(or the two-step variant) - The
chatmail-admin-web/build.rsseesCHATMAIL_ADMIN_WEB_BUILDenv (or the default path) and copiesindex.html+ assets intoembed/ - Cargo then
include_bytes!or equivalent at compile time - At runtime the SPA is served from memory
If you forget step 3, the binary will still run but /admin will show a placeholder telling you to build the UI.
Release Static Binary
scripts/build-release-static.sh:
- Runs
make build-admin-web - Does
cargo rustc ... -C target-feature=+crt-static - Verifies with
lddthat the binary is fully static - Result can be copied to a plain Debian 12 box (or similar) and will run without matching glibc
This is the artifact that gets signed and scp'd in production deploys.
Testing Pyramid
- Unit — inside each crate (
cargo test -p chatmail-foo) - Integration —
tests/workspace member (boots real servers, speaks SMTP/IMAP, exercises ctl) - E2E with Delta Chat —
make test-deltachat(real Delta Chat desktop + core clients against the Rust server in Incus VMs) - Throughput (T1) — special benchmark comparing madmail (Go) vs madmailv2 (Rust) under load
- Manual / relay-ping —
make test-dcloginagainst two real accounts on test servers
The E2E suite is the main integration check for "does this still work like a chatmail server should?"
Continuous Integration (implied)
The repo expects:
cargo fmt -- --checkcargo clippy -- -D warningscargo test --workspace- The heavy E2E and T1 jobs are usually run manually or on dedicated hardware because they need incus + time.
.env and Overrides
Many Makefile variables can be overridden via .env (sourced at the top of the Makefile):
STATE_DIR,CONFIGREMOTE1,REMOTE2DCLOGIN1,DCLOGIN2PRIV_KEY_FILEADMIN_WEB_DIR
This lets you point at different test servers or local state without editing the Makefile.
Next
Now you know how to build and ship the thing.
The next document explains the giant context/ and external/ directories that are not part of the shipping product but are essential for development and understanding.