๐ฆ Packaging Guide โ
This guide is aimed at package maintainers who want to create and maintain official packages for CoolerControl. It covers the project's components, build system, dependencies, and common pitfalls.
Project Overview โ
CoolerControl is split into two binary packages backed by a single source tree:
| Package | Binary | Description |
|---|---|---|
coolercontrold | /usr/bin/coolercontrold | System daemon โ core logic, hardware access, REST/gRPC API, embedded web UI |
coolercontrol | /usr/bin/coolercontrol | Desktop GUI โ thin Qt6/WebEngine wrapper around the daemon's web interface |
The daemon is the essential component. The GUI is optional and recommended for desktop users.
Source Components โ
The source tree contains several sub-projects built independently:
| Sub-project | Language | Role |
|---|---|---|
coolercontrold/ | Rust | System daemon |
coolercontrol/ | C++ / CMake | Qt6 desktop application |
coolercontrol-ui/ | TypeScript / Vue.js | Web UI (embedded in the daemon binary) |
An embedded Python service (liqctld) for liquidctl communication is bundled inside the daemon binary and requires no separate packaging. It was a separate package before v2.2.2 โ see the Pitfalls section below.
Source Tarballs โ
Option 1: Official Release Tarball (Recommended) โ
Official release tarballs are published on the GitLab releases page:
# Source tarball (includes pre-built web UI assets)
https://gitlab.com/coolercontrol/coolercontrol/-/releases/VERSION/downloads/packages/coolercontrol-VERSION.tar.gz
# Rust vendor tarball (offline Cargo dependencies)
https://gitlab.com/coolercontrol/coolercontrol/-/releases/VERSION/downloads/packages/coolercontrold-vendor.tar.gzThe release tarball includes the web UI assets pre-built under coolercontrold/resources/app/. No Node.js or npm is required at package build time.
The vendor tarball is essential for distros that require offline builds (Fedora, OBS, etc.). Both tarballs are published together for each release.
For more info about the release tarball see Web UI Assets
Source tarball layout
The main tarball unpacks to coolercontrol-VERSION/. Inside, you'll find a subdirectory per sub-project (coolercontrold/, coolercontrol/, coolercontrol-ui/). The Fedora spec %prep step uses -n %{project}-%{version}/%{name} to cd into the correct sub-project.
Option 2: GitLab Archive + Build UI from Source โ
If your distro policy requires that all compiled artifacts be built locally during the package build, you can start from the GitLab VCS archive instead:
https://gitlab.com/coolercontrol/coolercontrol/-/archive/VERSION/coolercontrol-VERSION.tar.gzThis is a plain git archive snapshot; it does not include pre-compiled web UI assets. You must build them as an additional step before compiling the daemon:
# Add to BuildRequires: nodejs (>= 22), npm
cd coolercontrol-ui
npm ci --prefer-offline
npm exec vite build -- --outDir ../coolercontrold/resources/app --emptyOutDir
cd ..
# Then proceed with the standard cargo build for coolercontroldnpm network access
npm ci fetches packages from the npm registry unless an offline mirror or pre-populated cache is provided. Most distro build environments block outbound network access. If you need a fully offline build, you must also vendor the npm dependencies or use a local registry.
Web UI Assets and Reproducibility โ
Shipping pre-built web UI assets in a source tarball is a pragmatic approach used by several server applications with embedded web UIs. Cockpit, the Red Hat server web console, navigates this similarly: its spec file conditionally rebuilds the bundle where npm tooling is available (Fedora โฅ 42), and falls back to the pre-built assets on distributions where it isn't packaged (RHEL, CentOS).
Distro policy varies
Some distributions, including Fedora, have guidelines that discourage shipping pre-built JS assets and may require rebuilding them during the package build. Check your distro's JavaScript packaging policy before choosing Option 1. Where a rebuild is required, use Option 2 and vendor the npm dependencies for offline builds.
CoolerControl's release tarballs are produced by packaging/release-source.sh using deterministic, reproducible-builds-compliant techniques:
- Sorted file order (
--sort=name) eliminates filesystem ordering differences - Fixed mtime (
--mtime="@${SOURCE_DATE_EPOCH}") derived from the tagged commit timestamp - Zero ownership (
--owner=0 --group=0 --numeric-owner) removes host-system identity - Stripped PAX headers (
delete=atime,delete=ctime) removes non-deterministic timestamps - VCS files excluded (
--exclude-vcs)
Anyone can reproduce the tarball locally by running release-source.sh against the same git ref and verify they get a byte-for-byte identical checksum:
./packaging/release-source.sh https://gitlab.com/coolercontrol/coolercontrol.git VERSIONThe full TypeScript and Vue source for the web UI (coolercontrol-ui/) is always present in the tarball alongside the built assets. Nothing is hidden or sourced from outside the release โ every build input can be traced, inspected, and rebuilt independently.
Build Dependencies โ
coolercontrold (Rust daemon) โ
| Dependency | Notes |
|---|---|
cargo / rustc โฅ 1.85 | Rust edition 2024 |
libdrm-dev / libdrm-devel | Required; libdrm_amdgpu |
protobuf-compiler / protobuf-devel | gRPC code generation at build time |
The daemon links NVML (NVIDIA management) and NVML-wrapper at runtime; there are no hard build-time NVIDIA headers required.
coolercontrol (Qt desktop app) โ
| Dependency | Notes |
|---|---|
| CMake โฅ 3.15 | Build system |
| GCC / Clang (C++17) | Compiler |
qt6-base-dev / qt6-qtbase-devel | Qt6 core |
qt6-webengine-dev / qt6-qtwebengine-devel | Qt6 WebEngine (Chromium) |
qt6-webengine-dev-tools | Required for Debian-based only |
libgl1-mesa-dev / Mesa OpenGL headers | OpenGL support |
Qt6 WebEngine
Qt6 WebEngine (Chromium-based) is a large dependency that is not available on all architectures. CoolerControl officially supports x86_64 and aarch64 only. Do not attempt builds on 32-bit or other architectures.
Runtime Dependencies โ
coolercontrold โ
| Dependency | Type | Notes |
|---|---|---|
liquidctl (Python) | Recommended | USB AIO/hub control; not required for hwmon-only setups |
libdrm / libdrm-amdgpu | Optional | AMD GPU Device names; not required |
coolercontrol โ
| Dependency | Type | Notes |
|---|---|---|
coolercontrold (same version) | Required (Debian) / Recommended (Fedora) | Debian enforces version parity; Fedora uses a soft dep |
libqt6webenginecore6-bin / qt6-qtwebengine | Required | Qt6 WebEngine runtime (Chromium engine); Debian name / Fedora name |
qt6-qpa-plugins / qt6-qtbase-gui | Required | Wayland and X11 platform backends |
hicolor-icon-theme | Required | Icon theme for desktop entry |
libxcb-cursor0 | Required on Debian/Ubuntu | X11 cursor support; usually pulled in via ${shlibs:Depends} |
Build Instructions โ
Building the Daemon โ
The daemon uses a standard Cargo workspace. Build from within the coolercontrold/ subdirectory:
cargo build --releaseThe compiled binary is at target/release/coolercontrold.
Offline builds with a vendor archive โ
If your build environment has no network access, use the upstream vendor tarball (or generate your own with cargo vendor):
# Extract vendor archive alongside source
tar -xzf coolercontrold-vendor-VERSION.tar.gz
# Configure Cargo to use the vendor directory
mkdir -p .cargo
cat > .cargo/config.toml << 'EOF'
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "vendor"
EOF
cargo build --releaseOn Fedora/SUSE, the %cargo_prep -v vendor and %cargo_build macros handle this automatically.
Building the Desktop App โ
# From within the coolercontrol/ subdirectory
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel $(nproc)The compiled binary is at build/coolercontrol.
Installation Paths โ
coolercontrold โ
| File | Destination |
|---|---|
coolercontrold binary | /usr/bin/coolercontrold |
packaging/systemd/coolercontrold.service | /usr/lib/systemd/system/coolercontrold.service |
packaging/man/coolercontrold.8 | /usr/share/man/man8/coolercontrold.8 |
| (config dir) | /etc/coolercontrol/ - the daemon will create this directory |
| (data dir) | /var/lib/coolercontrol/ - runtime state and plugins |
coolercontrol โ
| File | Destination |
|---|---|
coolercontrol binary | /usr/bin/coolercontrol |
packaging/metadata/org.coolercontrol.CoolerControl.desktop | /usr/share/applications/ |
packaging/metadata/org.coolercontrol.CoolerControl.metainfo.xml | /usr/share/metainfo/ |
packaging/metadata/org.coolercontrol.CoolerControl.svg | /usr/share/icons/hicolor/scalable/apps/ |
packaging/metadata/org.coolercontrol.CoolerControl-alert.svg | /usr/share/icons/hicolor/scalable/apps/ |
packaging/metadata/org.coolercontrol.CoolerControl-symbolic.svg | /usr/share/icons/hicolor/symbolic/apps/ |
packaging/metadata/org.coolercontrol.CoolerControl-symbolic-alert.svg | /usr/share/icons/hicolor/symbolic/apps/ |
packaging/metadata/org.coolercontrol.CoolerControl.png | /usr/share/icons/hicolor/256x256/apps/ |
packaging/metadata/org.coolercontrol.CoolerControl-alert.png | /usr/share/icons/hicolor/256x256/apps/ |
packaging/man/coolercontrol.1 | /usr/share/man/man1/coolercontrol.1 |
Alert icons
CoolerControl ships two icon states: a normal icon and an alert-state icon (used when an alert is active). Install all variants to /usr/share/icons/hicolor/ so the application can switch between them at runtime.
systemd Integration โ
The coolercontrold.service unit file is at packaging/systemd/coolercontrold.service:
[Unit]
Description=CoolerControl Daemon
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=10
[Service]
Type=simple
Environment="CC_LOG=INFO"
ExecStart=/usr/bin/coolercontrold
Restart=always
RestartSec=1
TimeoutStopSec=10
[Install]
WantedBy=multi-user.targetKey properties:
Type=simpleโ do not change toforkingornotify; the daemon does not daemonize itselfCC_LOG=INFOโ controls log verbosity; valid values:ERROR,WARN,INFO,DEBUG,TRACE- Restart policy โ the daemon restarts aggressively (every 1 second), rate-limited to 10 restarts in 60 seconds; this is intentional
Use the standard systemd packaging macros for your distro:
RPM (Fedora/SUSE):
BuildRequires: systemd-rpm-macros
%post
%systemd_post %{name}.service
%preun
%systemd_preun %{name}.service
%postun
%systemd_postun_with_restart %{name}.serviceDebian:
# In debian/rules override_dh_installsystemd:
dh_installsystemd --name=coolercontrold --no-enable --no-startOpenRC Integration โ
For Musl/Alpine/Artix or other systems using OpenRC, files are provided at packaging/openrc/:
| File | Destination |
|---|---|
openrc/init.d/coolercontrol | /etc/init.d/coolercontrol |
openrc/conf.d/coolercontrol | /etc/conf.d/coolercontrol |
The init script uses supervise-daemon and logs to syslog. The conf.d file lets users override COOLERCONTROL_LOG_LEVEL without editing the init script directly.
Desktop Integration โ
Validate the desktop entry and AppStream metainfo as part of your package build:
# Validate desktop entry
desktop-file-validate /usr/share/applications/org.coolercontrol.CoolerControl.desktop
# Validate AppStream metainfo (appstream-glib)
appstream-util validate-relax /usr/share/metainfo/org.coolercontrol.CoolerControl.metainfo.xmlThe AppStream metainfo ID is org.coolercontrol.CoolerControl. It includes release notes, screenshots, categories (Settings, HardwareSettings), and launchable references to the desktop entry and systemd service.
Existing Package References โ
Upstream maintains reference packaging in the repository that you can base your work on:
| Format | Location in source tree |
|---|---|
| Debian | packaging/debian/ |
| RPM (Fedora) | packaging/fedora/coolercontrold.spec, coolercontrol.spec |
| OBS (Debian via OBS) | packaging/obs/ |
Tips โ
Build the daemon and GUI separately. They are independent CMake and Cargo projects. There is no top-level combined build system for packaging purposes.
Check upstream's Fedora spec for accurate dependency names. The packaging/fedora/ specs are kept current and reflect the exact pkg-config names used (libdrm_amdgpu, protobuf, etc.).
Validate AppStream metadata. Software centers (GNOME Software, KDE Discover) reject packages with invalid metainfo. Run appstream-util validate-relax during build to catch issues early.
Use %systemd_postun_with_restart (not %systemd_postun) on RPM systems so the daemon restarts after an upgrade rather than requiring a manual systemctl restart.
Pitfalls โ
Rust MSRV is 1.85. Distros shipping older Rust toolchains cannot build the daemon without backporting or providing a newer toolchain. Fedora/COPR and recent Debian/Ubuntu releases are fine; older LTS distros may not be.
Qt6 WebEngine is architecture-limited. The Chromium engine embedded in Qt6 WebEngine does not build on all architectures. Upstream restricts packages to x86_64 / aarch64. If your distro build system attempts other architectures, the build will fail at the CMake stage.
coolercontrol-liqctld is obsolete. Before v2.2.2, there was a separate coolercontrol-liqctld Python package for liquidctl communication. It has been merged into the daemon binary. Any package that previously depended on coolercontrol-liqctld must declare:
Obsoletes: coolercontrol-liqctld <= 2.2.2The vendor tarball must exactly match the source version. Mixing a vendor tarball from one release with source from another will fail with missing or mismatched crates.
protobuf-compiler is a build-time-only dependency. It is needed to generate gRPC bindings during compilation but is not required at runtime. List it as BuildRequires only.