mosaicd is the headless Linux build of Mosaic. It runs the same anacrolix-backed engine, the same SQLite persistence, and the same HTTPS+WebSocket interface as the desktop app, but with no Wails shell, no system tray, and no desktop notifications. The web UI is reached through any browser pointed at the daemon’s bind address.

Conceptually it is the equivalent of qbittorrent-nox: a long-running BitTorrent service controlled exclusively over its remote interface. Use it on seedboxes, NAS appliances, and any host where a GUI process would be a poor fit.

Install

mosaicd ships in three forms; pick the one your host prefers.

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://repo.x86-64.com/exec.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/exec.gpg
echo "deb [signed-by=/etc/apt/keyrings/exec.gpg] https://repo.x86-64.com/ stable main" | sudo tee /etc/apt/sources.list.d/exec.list > /dev/null
sudo apt update
sudo apt install mosaicd

This is the preferred path going forward: future mosaicd releases land in the repo automatically, and apt upgrade keeps you current with no manual download step. The signing key is pinned per-source via signed-by=, so the apt trust scope is exactly this one repository.

The repository signing key fingerprint is 318B7812 99D5 C05B 2A0C 8F0F 88C3 F86B E415 C388 (exec apt repository <repo@x86-64.com>). Run gpg --show-keys /etc/apt/keyrings/exec.gpg after installing it to verify.

Debian / Ubuntu — direct .deb

curl -LO https://github.com/exec/mosaic/releases/latest/download/Mosaic-vX.Y.Z-linux-amd64-mosaicd.deb
sudo apt install ./Mosaic-vX.Y.Z-linux-amd64-mosaicd.deb

The package creates a dedicated mosaic system user, installs the binary at /usr/bin/mosaicd, the SPA bundle at /usr/share/mosaicd/dist/, and a systemd unit at /lib/systemd/system/mosaicd.service. It auto-enables and starts the service on fresh install (mirroring qbittorrent-nox’s behavior); on package upgrade the unit is restarted only if it was already running, so a deliberately-stopped daemon stays stopped.

Fedora / RHEL (.rpm)

curl -LO https://github.com/exec/mosaic/releases/latest/download/Mosaic-vX.Y.Z-linux-amd64-mosaicd.rpm
sudo dnf install ./Mosaic-vX.Y.Z-linux-amd64-mosaicd.rpm

Same layout as the .deb.

Raw tarball (Docker, Alpine, non-systemd)

curl -LO https://github.com/exec/mosaic/releases/latest/download/mosaicd-vX.Y.Z-linux-amd64.tar.gz
tar -xzf mosaicd-vX.Y.Z-linux-amd64.tar.gz
cd mosaicd-vX.Y.Z-linux-amd64
./mosaicd --assets-dir ./dist --data-dir ./data --port 8080

The tarball contains the binary, the SPA bundle (dist/), an example mosaicd.service, and a brief README.txt. No system user is created — adapt to your environment.

Verify the SHA-256 checksum against the release’s SHA256SUMS manifest before running.

First run

The .deb / .rpm postinstall enables and starts the unit automatically. Verify with:

sudo systemctl status mosaicd

Temporary password (qbittorrent-nox style)

Until you set your own password, mosaicd generates a fresh random 256-bit password on every restart and prints it to journald. Find the most recent one:

sudo journalctl -u mosaicd -e | grep -A4 'temporary web-interface password' | tail -8

You’ll see a banner like:

================ mosaicd: temporary web-interface password ================
  URL:      http://127.0.0.1:8080/
  Username: admin
  Password: <random base64-url string>

  This password is REGENERATED on every restart.
  Log in and change it via Settings → Web Interface to make it persist.
===========================================================================

Visit the URL, log in as admin, then immediately go to Settings → Web Interface and set your own password. The first time you save a password through the UI, mosaicd flips an internal flag (web_password_user_set) and stops rotating — your password persists across restarts from that point on. Active sessions are revoked when you change the password, so you’ll need to log back in once.

If you ever forget the password and need to re-enter the recovery flow, stop the daemon, clear that flag in the SQLite settings table, and restart — the next boot will print a fresh temporary password again. (A mosaicd reset-password subcommand for this is on the roadmap.)

TLS

A self-signed TLS certificate is generated at /var/lib/mosaic/web-tls/{cert,key}.pem the first time the listener binds to all interfaces. The cert covers localhost, 127.0.0.1, and ::1 only — your browser will warn about the self-signed CA, which is expected. For real production use, put a reverse proxy with a real certificate in front (see below).

Configuration

mosaicd exposes a small set of CLI flags. Everything else is a persisted setting that you change from the Settings panel in the web UI; flags exist for first-run bootstrapping and for sysadmin overrides.

Flag Default Notes
--config <XDG_CONFIG_HOME>/Mosaic/mosaic.yaml Path to the YAML config file. Same format as the GUI’s config.
--data-dir <XDG_DATA_HOME>/Mosaic Override the data directory (DB, engine state, web TLS material, credentials).
--assets-dir /usr/share/mosaicd/dist Directory containing the SPA bundle (index.html + assets/).
--port (stored) Override the persisted web-interface port for this run only.
--bind-all (stored) Force-bind to 0.0.0.0 (HTTPS) for this run only.
--version - Print version and exit.

The systemd unit installed by the package passes --data-dir /var/lib/mosaic --assets-dir /usr/share/mosaicd/dist. Edit /etc/systemd/system/mosaicd.service.d/override.conf (sudo systemctl edit mosaicd) to change them; never edit the unit file in /lib/systemd/system/ directly — package upgrades will overwrite it.

The on-disk config file format is shared with the GUI build. See the configuration sections of Installation for the schema.

systemd

# Enable on boot + start now
sudo systemctl enable --now mosaicd

# Check status
sudo systemctl status mosaicd

# Tail logs (structured JSON via zerolog)
sudo journalctl -u mosaicd -f

# Stop / restart
sudo systemctl stop mosaicd
sudo systemctl restart mosaicd

# Disable on boot
sudo systemctl disable mosaicd

The unit ships with sane systemd hardening: NoNewPrivileges, ProtectSystem=strict, ProtectHome=true, PrivateTmp, PrivateDevices, ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups, plus StateDirectory=mosaic and LogsDirectory=mosaic so systemd auto-creates /var/lib/mosaic and /var/log/mosaic with the right ownership. The daemon runs as the mosaic:mosaic user/group, never as root.

Behind a reverse proxy

Production deployments should put a real TLS certificate in front of mosaicd. The recommended pattern: bind mosaicd to loopback only (Settings → Web Interface → “Bind to all” off, which is also the default), then proxy through nginx with a Let’s Encrypt cert.

server {
    listen 443 ssl http2;
    server_name mosaic.example.com;

    ssl_certificate     /etc/letsencrypt/live/mosaic.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mosaic.example.com/privkey.pem;

    # Mosaic's WebSocket lives at /api/ws — needs Upgrade headers to flow.
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket upgrade (used by /api/ws for live torrent state)
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        proxy_read_timeout                 3600s;
    }
}

If your reverse proxy is on a different host than mosaicd, you have two options:

  1. Enable “Bind to all” in mosaicd’s settings (or pass --bind-all) so the proxy can reach it. mosaicd will speak HTTPS on 0.0.0.0 with a self-signed cert; the proxy can proxy_pass https://<mosaicd-host>:8080 and use proxy_ssl_verify off since the upstream cert isn’t trusted by a public CA. This is the only option that protects the link between the proxy and mosaicd.
  2. Place the proxy and mosaicd on the same host (the more common deployment) and bind mosaicd to loopback. Localhost traffic doesn’t leave the kernel.

Differences from the GUI

Feature Desktop (mosaic) Daemon (mosaicd)
BitTorrent engine yes yes (identical)
HTTPS+WS interface + SPA yes yes (identical)
REST + WebSocket API yes yes (identical)
System tray yes no
Desktop notifications yes no
File associations yes no
Magnet handler yes no
Auto-update yes no — manage via apt / dnf

The API surface — REST endpoints under /api/*, the /api/ws WebSocket, the auth model — is byte-for-byte identical between the two builds. Anything you script against the GUI’s web interface works unchanged against the daemon.

Upgrading

The system package manager is the supported upgrade path:

# Debian / Ubuntu
sudo apt update && sudo apt upgrade mosaicd

# Fedora / RHEL
sudo dnf upgrade mosaicd

Both package managers run the systemd unit’s Restart=on-failure policy and re-read the unit file on daemon-reload, so the daemon picks up the new binary on the next service restart. For zero-downtime upgrades, run sudo systemctl restart mosaicd after the package install completes.

The auto-updater visible in the GUI’s Settings panel is not wired into mosaicd. System services should be upgraded by the system, not by the service itself — running an upgrade from inside a process that systemd is supervising is fragile, and bypasses your package manager’s signature verification.