Guide: Cargo via Docker on macOS
Guide: Cargo via Docker on macOS
This guide describes what is in place for running Cargo (build, test, check) via Docker on macOS and how to use it when native Cargo is problematic (e.g. Rust version mismatch, tss-esapi or other platform issues).
What’s in place
All commands below are run from the repository root unless noted.
Prerequisites
-
Docker
Docker Desktop for Mac (or another Docker runtime) installed and running. -
cargo-make
Used to run the Makefile tasks. Install if needed: -
CARGO_HOME (optional but recommended)
So the container can reuse your Cargo cache:
Colima configuration (Apple Silicon M1/M2/M3)
If you use Colima on an M3 (or M1/M2) Mac, these settings can make cargo-docker-minimal and tests run faster.
Recommended start command (one-liner)
Use colima stop first if Colima is already running, then run the command above. This gives native arm64, 4 CPUs, 12 GiB RAM (avoids linker OOM when building carbide-api tests), VZ virtualization, and virtiofs mounts. Adjust --cpu and --memory to match your machine.
1. Use native arm64 (default)
Do not start Colima with --arch x86_64. The minimal image is built for your Mac’s architecture (arm64), so it runs natively. Forcing x86_64 would use emulation and be much slower.
2. Give the VM more CPU and memory
Rust builds are CPU- and memory-heavy. Defaults (e.g. 2 CPU, 2 GiB RAM) are too low. Example for an M3:
Adjust to your machine (e.g. 6–8 CPU, 8–12 GiB RAM if you have it).
3. Use VZ runtime and virtiofs (macOS 13+)
Apple’s VZ virtualization and virtiofs mounts are faster than the default QEMU + 9p/sshfs setup:
Or edit the config and start:
In the editor, set (or add) something like:
Then save, exit, and run colima start.
4. Rebuild the minimal image after changing Colima
If you change CPU/memory or VM type, rebuild so the image uses the new VM:
Summary
The minimal image uses the lld linker (lower RAM use than default ld). If linking still fails with signal 9, give Colima more memory (e.g. --memory 12), then colima stop and colima start again, and rebuild the image: cargo make build-cargo-docker-image-minimal.
Tests to run before build
Use these to validate changes (e.g. IB partition update) before building the Docker image or doing a full build.
When to run what
Use the smoke test for quick feedback; run the IB partition tests when you need to validate that feature (e.g. before a PR). The minimal image uses sccache and a persistent cache dir (~/.sccache or SCCACHE_DIR), so the second and later runs of the same (or similar) commands are much faster.
Option A: Quick check (no database)
Verifies that the API and deps compile. No Postgres needed.
If you haven’t built the minimal image yet, build it first: cargo make build-cargo-docker-image-minimal.
Option A2: Postgres connectivity smoke test (small, fast)
Verifies that the minimal Docker setup can reach Postgres: DATABASE_URL is passed into the container and the DB is reachable (e.g. via host.docker.internal). Uses the small postgres-smoke-test crate so it compiles quickly.
1. Start Postgres (e.g. docker-compose up -d postgresql or use your existing Postgres). If you get an error about the loki logging plugin, start Postgres without it: docker-compose -f docker-compose.yml -f docker-compose.no-loki.yml up -d postgresql.
2. Run the smoke test:
Use the correct host/port (e.g. host.docker.internal:30432 if Postgres is on port 30432 on the host). The cargo-docker-minimal task adds --add-host=host.docker.internal:host-gateway so the container can reach the host (required on Colima; harmless on Docker Desktop). If the test passes, Postgres connectivity from the container is working.
Quick one-liner (recommended for a fast sanity check):
Takes ~10–30 seconds. Use this instead of the long carbide-api DB tests when you only need to confirm Docker + Postgres work.
Option B: IB partition tests (need PostgreSQL)
These tests use a real database and take a long time (first run compiles most of the workspace). Prefer Option A2 (smoke test) for a quick check; run these when you need to validate IB partition (or other API) behavior.
1. Start Postgres (if not already running):
If you get an error about the loki logging plugin, use: docker-compose -f docker-compose.yml -f docker-compose.no-loki.yml up -d postgresql.
2. Run IB partition tests:
Locally (Rust 1.90 + DATABASE_URL):
Via minimal Docker image (after build-cargo-docker-image-minimal):
Use host.docker.internal so the container can reach Postgres on the host (Docker Desktop for Mac supports this; the minimal image includes libpq-dev for the Postgres client).
DATABASE_URL format: Use the full URL including the database name (e.g. .../carbide_development). The test harness connects to that database to create and drop per-test databases (e.g. db0_carbide__tests__...). The database you name in the URL must already exist (create it or use an existing one such as carbide_development).
Other test filters: The test name is a substring match. Examples:
Suggested order before a build
- Run Option A (check) to ensure everything compiles.
- If you changed API or DB code, run Option B (ib_partition tests) with Postgres and
DATABASE_URLset. - Then run your full build (e.g.
cargo make cargo-docker-minimal -- build -p carbide-admin-cli --release).
Quick start (recommended on Mac)
Use the minimal path. The workspace (including carbide-rpc) needs protoc to compile .proto files, so you build a small image once (~2–5 min) that adds only Rust + protoc.
First time only — build the minimal image:
Then run Cargo as needed:
Output binaries appear under target/ in your repo as usual.
How to use: Minimal path (cargo-docker-minimal)
When to use: Day-to-day builds and checks on macOS when you don’t need the full API test stack (PostgreSQL, TSS, etc.).
What it uses: The image carbide-build-minimal (Rust 1.90 + protoc). You must build it once with build-cargo-docker-image-minimal (~2–5 min). The carbide-rpc crate needs protoc and the Google well-known proto files (libprotobuf-dev), so the bare rust:1.90.0-slim-bookworm image is not enough for workspace builds.
Usage
What works
- Building carbide-admin-cli.
- Building/checking carbide-api with
--no-default-features(avoidstss-esapiand other heavy deps). - Other crates that don’t need PostgreSQL, protobuf, or TSS.
What doesn’t work
- Full carbide-api with default features (needs TSS/measured-boot stack).
- API tests that require a database (slim image has no PostgreSQL client libs). For those, use the full image path below or run tests elsewhere (e.g. CI).
How to use: Full image path (build-cargo-docker-image + cargo-docker)
When to use: When you need the full environment (e.g. run API tests with PostgreSQL, or build with default features). On Apple Silicon the image build is slow (45+ minutes); prefer the minimal path for routine work.
Step 1: Build the image (once)
Or manually:
On Apple Silicon this runs under emulation and can take a long time; step 7 (installing cargo tools) alone often takes 45+ minutes.
Step 2: Run Cargo in the full container
For tests that need Postgres, start it first (e.g. docker compose up -d postgresql) and use host.docker.internal in DATABASE_URL so the container can reach the host’s Postgres.
Command reference
When to use which option
Tips and troubleshooting
-
Cargo cache
The tasks mountCARGO_HOME(e.g.$HOME/.cargo) into the container so dependency builds are cached on your Mac. -
sccache (faster repeat builds)
The minimal image usessccacheand mounts~/.sccache(orSCCACHE_DIR) so compiled Rust artifacts are reused across runs. Rebuild the minimal image once (cargo make build-cargo-docker-image-minimal) to get sccache; then the second and later runs oftest -p carbide-api ...(and other cargo commands) are much faster. -
File ownership
cargo-docker-minimalruns the container as root and runschown -Ron/codeafter each run, sotarget/is left owned by your user and “Permission denied” when writing totarget/should not recur.
If you still see “Permission denied” (e.g. after using another Docker task that writes to the repo as root), from the repo root run:or manually:
sudo chown -R $(id -u):$(id -g) target(or.for the whole repo). -
Postgres for API tests
Start Postgres (e.g.docker-compose up -d postgresql; if the Loki plugin is missing, use-f docker-compose.no-loki.ymlas well), then set:Use
host.docker.internal(notlocalhost) so the container can reach Postgres on the host.cargo-docker-minimalpassesDATABASE_URLinto the container when set. -
Docker or tests hang
- DB tests: If
DATABASE_URLis unset or useslocalhost, the container cannot reach Postgres and tests can hang. SetDATABASE_URLwithhost.docker.internalas the host (see above). - Stuck containers: Stop them with
docker psthendocker stop <container_name>ordocker rm -f <container_name>. - Timeout: Run tests with a time limit so they don’t hang indefinitely:
- Linux:
timeout 300 cargo make cargo-docker-minimal -- test ... - macOS: Install GNU coreutils then use
gtimeout, or run without timeout and setDATABASE_URLso DB tests don’t hang:
- Linux:
- DB tests: If
-
Apple Silicon (M1/M2/M3) and Colima
The full build image is x86_64 and runs under emulation, so it is slow. Use cargo-docker-minimal for daily work; use the full image only when you need it.
Colima: For faster runs on M3, give the VM more resources and use the VZ runtime with virtiofs (see Colima configuration below). -
protocrequired for workspace builds
Thecarbide-rpccrate compiles.protofiles and needs the Protocol Buffers compiler (protoc). The minimal image adds only that; the full image includes it as well. -
Running without cargo-make
You can run the samedocker runlocally. The minimal variant (after buildingcarbide-build-minimalonce); add-e DATABASE_URL="$DATABASE_URL"when running tests that need the DB: