Upload the deployed binary's debug artifact to resolve function names, files, lines, and inline calls. Add a source bundle for code excerpts. Exceptions still arrive without either upload.
This guide covers Linux x86_64 ELF/DWARF. Your CI job needs stable Rust, GNU binutils, and Node.js 22 or later.
The examples use a binary named api. Replace its path with your actual build output.
Prepare CI
Create a project token with Deploy scope in
the project receiving your exceptions. Store it as MONOCLE_API_KEY in CI secrets. The DSN cannot upload artifacts.
The upload tools currently live in the Monocle repository. Check them out outside your application's source tree:
export MONOCLE_TOOLS="$(mktemp -d)"
git clone --filter=blob:none --no-checkout \
https://github.com/monocle-sh/monocle.git "$MONOCLE_TOOLS"
git -C "$MONOCLE_TOOLS" checkout --detach \
116b7e603bd197b31e73e2bde17b2e55840c4133Keep MONOCLE_TOOLS across CI steps. In GitHub Actions, persist it through GITHUB_ENV.
Build and extract symbols
Add this release profile in your application's workspace root:
[profile.release]
debug = 1
strip = falseBuild once, extract the debug artifact, then strip that executable:
cargo build --release --locked --bin api
objcopy --only-keep-debug target/release/api target/release/api.debug
objcopy --compress-debug-sections=zlib target/release/api.debug
strip --strip-all target/release/api
binary_id=$(readelf -n target/release/api | sed -n 's/.*Build ID: //p')
debug_id=$(readelf -n target/release/api.debug | sed -n 's/.*Build ID: //p')
test -n "$binary_id" && test "$binary_id" = "$debug_id"Deploy this stripped executable. Do not rebuild it after extracting symbols. For Docker, extract the artifacts from the builder stage that produces your runtime executable. Monocle matches the full GNU build ID, not the filename or release.
Include sources
Skip this step for file and line resolution without code excerpts. To include source text, collect it from the unchanged checkout used for the build:
cargo run --release --locked \
--manifest-path "$MONOCLE_TOOLS/apps/symbolicator/Cargo.toml" \
--bin monocle-native-sources -- \
target/release/api.debug target/release/api.sources.zip --root "$PWD"The collector includes files referenced by DWARF beneath --root. Review the included and skipped counts.
For a build using --remap-path-prefix, pass its DWARF-side prefix with --prefix /remapped/root.
Source bundles contain your private code. Review their contents before upload. Never include credentials in a bundle or pass the project token through Docker build arguments.
Upload artifacts
Expose MONOCLE_API_KEY only to the upload step:
node "$MONOCLE_TOOLS/scripts/upload-native-symbols.mjs" \
target/release/api.debug target/release/api.sources.zipOmit the ZIP argument for symbols only. Success prints Native debug artifact ready: elf:<build ID>
and, with sources, Native source bundle ready: elf:<build ID>. Fail the job if the command exits nonzero.
Keep both files out of the runtime image.
For self-hosted Monocle, set MONOCLE_API_URL to your API. During an upgrade from an API without
native upload routes, add --wait-seconds 300 before the filenames to wait for a compatible rollout.
Check the result
Capture one test error with monocle::capture_error in a controlled environment, then open its issue.
Your frames should show functions, files, and lines. With sources uploaded, they also show code excerpts.
- Only raw frames: check the build IDs. A cold cache can need another read after loading finishes.
- Files and lines, but no code: check the source collector's included files and path mapping.
- Some unresolved frames: optimization can remove information, and your artifact does not cover system libraries.
Late uploads enrich existing occurrences when you read them, without changing their captured data or grouping.
Retries reuse artifacts. Use --force before the filenames to replace an incorrect artifact for the same build ID.
Uploads are private to the project and share its source map quota. They expire after 90 days without use. Debug files are limited to 200 MiB. Source ZIPs and total source text are each limited to 50 MiB, with 1 MiB per source file. Mach-O, PDB, minidumps, and split DWARF sidecars are not supported.