aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-27 18:35:58 +0100
committersalmonik <salmonik@tuta.io>2026-10-27 18:35:58 +0100
commita82489c95f8c8f3e55f92939224599598bd0de96 (patch)
tree59591c88cede5a3aef8255fad713ddd9b329709c
parentmusic: group playback commands under /music (diff)
downloadsenator-sperma-a82489c95f8c8f3e55f92939224599598bd0de96.tar.gz
senator-sperma-a82489c95f8c8f3e55f92939224599598bd0de96.zip
docker: build and run on Debian trixie
Add a multi-stage image that compiles inside Debian trixie and runs on the same glibc/libssl/libopus, avoiding the Arch-built binary failing on Debian. The runtime image installs ffmpeg, qalc, the shared libraries, and the latest yt-dlp zipapp, and runs as uid 1000. compose.yaml mounts data/ and piper/, overrides TTS_MODEL_PATH for the container layout, and builds with host networking because the default Docker bridge DNS cannot resolve crates.io or deb.debian.org on this host.
-rw-r--r--.dockerignore12
-rw-r--r--Dockerfile76
-rw-r--r--README.md41
-rw-r--r--compose.yaml27
4 files changed, 156 insertions, 0 deletions
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..0a22f10
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,12 @@
1.git
2.gitignore
3.zed
4.venv
5target
6data
7piper
8models
9.env
10docs
11tests
12*.wav
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..eaedc7f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,76 @@
1# Build on the same Debian release as the runtime image. This pins glibc,
2# libssl and libopus, so a binary built on a rolling-release host (Arch, etc.)
3# no longer fails on Debian due to newer toolchain/glibc symbols.
4FROM rust:1-trixie AS builder
5
6ENV DEBIAN_FRONTEND=noninteractive
7
8# Build dependencies:
9# - cmake + clang + libclang-dev: espeak-rs-sys (pulled in by piper-rs) builds
10# espeak-ng and runs bindgen
11# - pkg-config + libopus-dev: libopus_sys
12# - libssl-dev: native-tls / openssl-sys
13RUN apt-get update \
14 && apt-get install -y --no-install-recommends \
15 cmake \
16 clang \
17 libclang-dev \
18 pkg-config \
19 libopus-dev \
20 libssl-dev \
21 && rm -rf /var/lib/apt/lists/*
22
23WORKDIR /app
24
25# Compile the dependency graph once with a dummy main so source edits do not
26# invalidate the expensive piper/espeak/onnxruntime build.
27COPY Cargo.toml Cargo.lock build.rs ./
28RUN mkdir src \
29 && printf 'fn main() {}\n' > src/main.rs \
30 && cargo build --release \
31 && rm -rf src
32
33COPY src ./src
34RUN cargo build --release
35
36# ── Runtime ───────────────────────────────────────────────────────────
37FROM debian:trixie-slim
38
39ENV DEBIAN_FRONTEND=noninteractive
40
41# Runtime libraries reported by `readelf -d`: libstdc++, libopus, libssl,
42# libcrypto. ffmpeg/qalc/yt-dlp are invoked as subprocesses.
43# Full python3 (not python3-minimal) is required by the yt-dlp zipapp.
44RUN apt-get update \
45 && apt-get install -y --no-install-recommends \
46 ca-certificates \
47 ffmpeg \
48 libopus0 \
49 libssl3t64 \
50 libstdc++6 \
51 passwd \
52 python3 \
53 qalc \
54 tzdata \
55 && rm -rf /var/lib/apt/lists/*
56
57# yt-dlp is distributed as a self-contained Python zipapp; using the latest
58# release avoids the stale Debian package breaking on YouTube changes.
59ADD https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp /usr/local/bin/yt-dlp
60RUN chmod 755 /usr/local/bin/yt-dlp
61
62RUN useradd --create-home --uid 1000 --shell /usr/sbin/nologin bot \
63 && mkdir -p /app/data /app/piper /tmp/senator_sperma \
64 && chown -R bot:bot /app /tmp/senator_sperma
65
66WORKDIR /app
67COPY --from=builder /app/target/release/senator-sperma /usr/local/bin/senator-sperma
68
69USER bot
70
71ENV TZ=Europe/Warsaw \
72 TTS_MODEL_PATH=/app/piper \
73 RUST_LOG=info \
74 HOME=/home/bot
75
76ENTRYPOINT ["/usr/local/bin/senator-sperma"]
diff --git a/README.md b/README.md
index 41ef0c3..e52c1b9 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,47 @@ bot falls back to Google Translate TTS.
56cargo run --release 56cargo run --release
57``` 57```
58 58
59## Docker
60
61The image builds and runs on Debian trixie, so it does not depend on the
62host's gcc/glibc version. This is the recommended way to deploy on Debian
63when developing on a rolling-release distro.
64
65```bash
66docker compose up -d --build
67```
68
69Or without Compose:
70
71```bash
72docker build -t senator-sperma .
73docker run -d --name senator-sperma --restart unless-stopped \
74 --env-file .env \
75 -e TTS_MODEL_PATH=/app/piper \
76 -v "$PWD/data:/app/data" \
77 -v "$PWD/piper:/app/piper:ro" \
78 senator-sperma
79```
80
81Notes:
82
83- `data/` holds the SQLite database and is mounted for persistence. Create it
84 first (`mkdir -p data`) so it is owned by your user rather than root.
85- `piper/` holds the Piper model and is mounted read-only; the container
86 overrides `TTS_MODEL_PATH` to `/app/piper`, so the absolute host path in
87 `.env` is harmless. Put the `.onnx` and `.onnx.json` files there (the same
88 directory used for local runs).
89- The container runs as uid 1000. Make sure `data/` is writable by that uid.
90- `TZ` defaults to `Europe/Warsaw` for cron jobs; override it in
91 `compose.yaml` or with `-e TZ=...` if needed.
92- If Copyparty runs on the host, use `http://host.docker.internal:<port>` in
93 `.env` and enable the commented `extra_hosts` entry in `compose.yaml`.
94- If the build fails to resolve `crates.io` or `deb.debian.org`, run
95 `docker build --network=host -t senator-sperma .`; `compose.yaml` already
96 sets `network: host` for builds on this host.
97- yt-dlp is fetched from the latest upstream release at image build time.
98 Rebuild with `--no-cache` (`docker compose build --no-cache`) to refresh it.
99
59## Commands 100## Commands
60 101
61Slash commands are autoregistered globally on startup. Some are registered 102Slash commands are autoregistered globally on startup. Some are registered
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..eac326f
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,27 @@
1services:
2 senator-sperma:
3 build:
4 context: .
5 # This host's default Docker bridge DNS cannot resolve crates.io /
6 # deb.debian.org, so build on the host network. Remove this line if
7 # your Docker setup resolves DNS normally.
8 network: host
9 image: senator-sperma:latest
10 container_name: senator-sperma
11 restart: unless-stopped
12 init: true
13 env_file:
14 - .env
15 environment:
16 # The host .env points TTS_MODEL_PATH at an absolute host path, which
17 # does not exist inside the container. The model is mounted below.
18 TTS_MODEL_PATH: /app/piper
19 # Override if the bot should not use Polish local time for cron jobs.
20 # TZ: Europe/Warsaw
21 volumes:
22 - ./data:/app/data
23 - ./piper:/app/piper:ro
24 # If Copyparty runs on the host, uncomment so the container can reach it
25 # (then use http://host.docker.internal:<port> in .env):
26 # extra_hosts:
27 # - "host.docker.internal:host-gateway"