RPM packaging pipeline for mistral.rs on Fedora 43 / x86_64 with CUDA support.
This repo does not contain the mistral.rs source. It clones upstream at a given release tag, cross-compiles with CUDA, and produces signed RPMs published to a dnf repo at rpm.lair.cafe.
Two Gitea Actions workflows drive the pipeline:
rpm.lair.cafe. It also checks the upstream main branch HEAD and triggers prerelease builds for the unstable repo.mistralrs with flavour-specific CUDA features on a cuda-13.0 runner.rpmbuild.rpm.lair.cafe, and updates the repo metadata with createrepo_c.main, uses versioning from Cargo.toml with a prerelease release suffix (e.g. 0.8.1-0.1.20260511git1a2b3c4), and publishes to the unstable repo.Build flavours are defined in the workflow matrix. Each flavour targets a specific GPU generation with the same CUDA 13.0 toolkit and features (cuda, cudnn, flash-attn, nccl).
Currently defined:
| Flavour | Compute cap | GPU generation |
|---|---|---|
| ampere | sm_86 | RTX 3060, A2000–A6000 |
| ada | sm_89 | RTX 4060–4090, L40 |
| blackwell | sm_120 | RTX 5090, B100, B200 |
Each RPM installs a templated systemd unit ([email protected]). Instances are configured via environment files in /etc/mistralrs/:
# copy the example config
sudo cp /etc/mistralrs/default.conf.example /etc/mistralrs/mymodel.conf
# edit MISTRALRS_ARGS, HF_TOKEN, etc.
sudo systemctl start mistralrs@mymodel
The RPM repo is hosted on oolon (oolon.kosherinata.internal) behind nginx with TLS via Let's Encrypt. The setup scripts in script/setup/ are run once from a dev workstation with SSH access to oolon.
./script/setup/dns.sh
Creates a CNAME record for rpm.lair.cafe via the Cloudflare API. Requires a Cloudflare API token in ~/.cloudflare/lair.cafe.
./script/setup/cert.sh
Obtains a Let's Encrypt certificate for rpm.lair.cafe using the Cloudflare DNS challenge. Run on oolon.
./script/setup/nginx.sh
Syncs the nginx config to oolon, creates the gitea_ci system user with SSH access for CI publishing, sets up the RPM repo directory at /var/www/rpm/fedora/43/x86_64, and reloads nginx. Requires the gitea_ci SSH public key at ~/.ssh/id_gitea_ci.pub.
./script/setup/gpg.sh
Manages the RPM signing key in a dedicated keyring at ~/.gnupg/lair:
[email protected] if one doesn't exist.oolon:/var/www/rpm/<short-id>.gpg.After running the script, add two secrets to the Gitea repo:
| Secret | Value |
|---|---|
RPM_SIGNING_KEY | Output of gpg --homedir ~/.gnupg/lair --armor --export-secret-subkeys <subkey-fpr>! |
RPM_SIGNING_KEY_ID | [email protected] |
The trailing ! in the export command restricts the export to that specific subkey. Only the signing subkey is shared with CI; the master key stays on the workstation.
gpg --homedir ~/.gnupg/lair --quick-add-key <master-fpr> ed25519 sign 1y
Then update the RPM_SIGNING_KEY secret in Gitea with the new subkey. The public key served to users doesn't change since it's anchored to the master key.
Runners that run the publish job need sequoia-sq installed:
sudo dnf install sequoia-sq
sudo rpm --import https://rpm.lair.cafe/<short-id>.gpg
sudo tee /etc/yum.repos.d/lair-cafe.repo > /dev/null <<'EOF'
[lair-cafe]
name=lair.cafe RPM Repository
baseurl=https://rpm.lair.cafe/fedora/$releasever/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://rpm.lair.cafe/<short-id>.gpg
EOF
# install the package for your GPU generation
sudo dnf install mistralrs-ampere # RTX 3000 series
sudo dnf install mistralrs-ada # RTX 4000 series
sudo dnf install mistralrs-blackwell # RTX 5000 series
Unstable packages are built from the latest upstream main commit and published to a separate repo. The RPM release field uses the Fedora snapshot convention (e.g. 0.8.1-0.1.20260511git1a2b3c4.fc43) so stable releases automatically supersede any installed prerelease.
sudo tee /etc/yum.repos.d/lair-cafe-unstable.repo > /dev/null <<'EOF'
[lair-cafe-unstable]
name=lair.cafe RPM Repository (unstable)
baseurl=https://rpm.lair.cafe/fedora/$releasever/$basearch/unstable/
enabled=0
gpgcheck=1
gpgkey=https://rpm.lair.cafe/<short-id>.gpg
EOF
# install from unstable on demand
sudo dnf --enablerepo=lair-cafe-unstable install mistralrs-ada
To force a rebuild of an already-published RPM (e.g. after a packaging change), remove the RPM from the repo server and update the index:
ssh oolon "
sudo rm /var/www/rpm/fedora/43/x86_64/mistralrs-ada-<version>-1.fc43.x86_64.rpm \
&& cd /var/www/rpm/fedora/43/x86_64 \
&& sudo createrepo_c --update .;
"
The next poll-upstream cycle (every 15 minutes) will detect the missing package and trigger a full rebuild. You can also trigger poll-upstream manually from the Gitea Actions UI to avoid waiting.
Do not delete the RPM without running createrepo_c --update afterwards — this leaves the repo index referencing a missing file, which causes errors for dnf clients.
The build-release and build-prerelease workflows require the following secrets:
| Secret | Purpose |
|---|---|
DISPATCH_TOKEN | Gitea API token for triggering builds |
RPM_SIGNING_KEY | ASCII-armored GPG signing subkey |
RPM_SIGNING_KEY_ID | GPG key UID ([email protected]) |
RSYNC_SSH_KEY | SSH private key for the gitea_ci user |
90 activities
24a9d1b chore(poll): restore 6h upstream poll cadenceaae3cf3 chore(build): CARGO_TERM_COLOR=alwaysd322943 fix(build): pass -Wno-template-body to nvcc host compiler for gcc 15ca69a6f chore(build-prerelease): reduce build jobs and nvcc threadsdc7759f fix(poll): match run path with @ref suffix in in-flight guard16cae99 fix(poll): in-flight guard + 6h cycle to stop build cancellation livelock4f3b7e5 fix(poll): in-flight guard + 6h cycle to stop build cancellation livelockbf39c5b chore: ignore claude settings1af2d44 fix(nginx): drop duplicate application/x-rpm mime declaratione9abecc fix(ui): host .repo files and use URL form in install instructionse7d7e69 fix(ci): use rpm runner for package and publish jobs34c9cbb fix(ci): restore cancel-in-progress on prerelease buildse0cb337 fix(ci): reduce poll frequency to hourly and stop cancelling prerelease builds3e30908 fix(ci): separate concurrency groups so polling is not blocked by buildsc1ffe7e fix(ci): use upstream default branch master for prerelease polling0d6f48f docs: update readme and CLAUDE.md for per-GPU flavours and prereleaseace6037 feat: replace cuda13 flavour with per-GPU-generation packages661cf57 fix(ci): remove nvm dependency from deploy-ui workflowa79eafd feat: add prerelease RPM builds from upstream main branchfff56a6 Revert "fix(ci): clone upstream locally instead of using repo-url for changelog"23283c3 fix(ci): clone upstream locally instead of using repo-url for changelog9a316ba fix: remove all remaining mistralrs-server referencesef7e3a3 refactor: rename package from mistralrs-server to mistralrs3e4191a feat: switch from deprecated mistralrs-server to mistralrs CLIb6977ed feat(rpm): create mistralrs system user and group on installb8e568b refactor(rpm): simplify to single /usr/bin/mistralrs-server binary755f217 refactor(rpm): install binary to /usr/bin instead of /optd9cddb4 fix: remove explicit author override from changelog action291596c fix: set changelog author and remove hardcoded entry from spec61cdc53 fix(rpm): filter auto-detected CUDA library dependencies6647ed2 fix: use tee to write repo file instead of dnf config-manager10263e4 fix: support zstd-compressed repodata in packages.json generatorde96e7c fix: include standard mime.types in nginx config0ec89de fix(ci): remove nginx config deploy from deploy-ui workflow9f57342 fix(ci): use --recursive instead of --archive for UI deploy6dde360 fix(ci): trigger deploy-ui on changes to its own workflow filebb76402 fix(ci): add --omit-dir-times to UI rsync deploy38a875d feat: add nvm setup script for CI runners3603c31 fix(ci): use explicit NVM_DIR to load nvm in non-interactive shell7f9e857 feat: add React UI for rpm.lair.cafea6cebc7 docs: use sudo for repo server commands in rebuild instructions85b78d0 docs: add forced rebuild instructions to readme6d3dca1 feat(ci): generate rpm changelog from upstream mistral.rs commits6946682 fix(ci): export LIBRARY_PATH for CUDA linker search pathsff8e543 fix(ci): verify repo index consistency in poll-upstream check65a265c refactor(ci): inline build steps, delete build-binary.sh54cffcf fix(ci): move RPM_REPO_HOST to job-level env, remove step duplicationcacdbeb refactor(ci): use RPM_REPO_HOST env var, add SSH connectivity testba5eec7 refactor(ci): inline publish steps, delete publish-repo.she3c403e fix(ci): add progress markers to publish script for debugging087c5d5 fix(ci): add rpmmacros dump and explicit exit code capture for signing7df7362 fix(ci): use rpm 6 openpgp signing with sequoia-sqaffc382 fix(ci): remove gpg sign command override, let rpm-sequoia sign natively9f0116b fix(ci): override rpm sign command to use gpg backend3291f77 fix(ci): add gpg and rpmsign diagnostics to isolate signing failure7575ec6 fix(ci): set ultimate trust on imported signing keyecf38bb fix(ci): add diagnostics to signing step93d442d fix(ci): capture rpm --addsign output to file for error reporting33aa40e fix(ci): surface rpm --addsign error output in CI logs6ffbde4 fix(ci): strip trailing slash from RPM_DIR to avoid double slashese902729 fix(ci): remove rpm --checksig that requires rootbab7d98 fix(ci): use fedora-43 runner label for all non-build jobs70ae210 fix(ci): import public key for checksig and force dist tag override38e36e4 fix(ci): use gpg-agent loopback instead of custom sign command1919e14 fix(ci): move rpmmacros to template file94d890b fix(ci): remove job-level concurrency group from publishee693f6 fix(ci): revert runner array syntax unsupported by Giteaf4e1008 feat(ci): parameterize fedora version across pipeline0cb6a4f fix(ci): use heredoc for rpmmacros to avoid shell escaping issues4160334 fix(ci): remove duplicate gpg in sign command macro0147e0f fix(ci): configure gpg for non-interactive RPM signing82a04c8 fix(ci): flatten RPM artifacts before signing75806fd chore: see if we can squeeze a few more cycles out of the builderscdf6cdf docs: add repo readme with setup and usage instructions937e72b fix(ci): remove stale .rpmmacros before rpmbuild in package job8ceabed feat: add GPG key setup script and generalize nginx GPG key servinge6c2b4e fix(ci): prevent poll-upstream from cancelling in-progress buildsf2ed86b feat(ci): add CARGO_BUILD_JOBS and NVCC_THREADS to build matrix146af6a fix (ci): will have to debug this later40cd1a4 feat(ci): add NCCL dependency check with install instructions8f0bf36 fix(ci): add rustup install/update step to build jobf4e68d4 refactor(ci): replace dynamic matrix with static includes2785395 fix(ci): use python yq syntax for flavours parsingadb1683 chore(ci): remove debug logging from poll-upstream dispatchead42ae fix(ci): use full ref format and Content-Type header for dispatch09ca33e debug(ci): log dispatch URL and response body for 422 diagnosis8cffedd fix(ci): use PAT for workflow dispatch in poll-upstream95dec3d fix(ci): grant actions write permission to poll-upstream workflowc691687 fix(ci): remove --fail from curl in poll-upstream version check429bcbc fix: evaluate http response code for package existence