diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 18:35:58 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 18:35:58 +0100 |
| commit | a82489c95f8c8f3e55f92939224599598bd0de96 (patch) | |
| tree | 59591c88cede5a3aef8255fad713ddd9b329709c | |
| parent | music: group playback commands under /music (diff) | |
| download | senator-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-- | .dockerignore | 12 | ||||
| -rw-r--r-- | Dockerfile | 76 | ||||
| -rw-r--r-- | README.md | 41 | ||||
| -rw-r--r-- | compose.yaml | 27 |
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 | ||
| 5 | target | ||
| 6 | data | ||
| 7 | piper | ||
| 8 | models | ||
| 9 | .env | ||
| 10 | docs | ||
| 11 | tests | ||
| 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. | ||
| 4 | FROM rust:1-trixie AS builder | ||
| 5 | |||
| 6 | ENV 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 | ||
| 13 | RUN 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 | |||
| 23 | WORKDIR /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. | ||
| 27 | COPY Cargo.toml Cargo.lock build.rs ./ | ||
| 28 | RUN mkdir src \ | ||
| 29 | && printf 'fn main() {}\n' > src/main.rs \ | ||
| 30 | && cargo build --release \ | ||
| 31 | && rm -rf src | ||
| 32 | |||
| 33 | COPY src ./src | ||
| 34 | RUN cargo build --release | ||
| 35 | |||
| 36 | # ── Runtime ─────────────────────────────────────────────────────────── | ||
| 37 | FROM debian:trixie-slim | ||
| 38 | |||
| 39 | ENV 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. | ||
| 44 | RUN 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. | ||
| 59 | ADD https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp /usr/local/bin/yt-dlp | ||
| 60 | RUN chmod 755 /usr/local/bin/yt-dlp | ||
| 61 | |||
| 62 | RUN 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 | |||
| 66 | WORKDIR /app | ||
| 67 | COPY --from=builder /app/target/release/senator-sperma /usr/local/bin/senator-sperma | ||
| 68 | |||
| 69 | USER bot | ||
| 70 | |||
| 71 | ENV TZ=Europe/Warsaw \ | ||
| 72 | TTS_MODEL_PATH=/app/piper \ | ||
| 73 | RUST_LOG=info \ | ||
| 74 | HOME=/home/bot | ||
| 75 | |||
| 76 | ENTRYPOINT ["/usr/local/bin/senator-sperma"] | ||
| @@ -56,6 +56,47 @@ bot falls back to Google Translate TTS. | |||
| 56 | cargo run --release | 56 | cargo run --release |
| 57 | ``` | 57 | ``` |
| 58 | 58 | ||
| 59 | ## Docker | ||
| 60 | |||
| 61 | The image builds and runs on Debian trixie, so it does not depend on the | ||
| 62 | host's gcc/glibc version. This is the recommended way to deploy on Debian | ||
| 63 | when developing on a rolling-release distro. | ||
| 64 | |||
| 65 | ```bash | ||
| 66 | docker compose up -d --build | ||
| 67 | ``` | ||
| 68 | |||
| 69 | Or without Compose: | ||
| 70 | |||
| 71 | ```bash | ||
| 72 | docker build -t senator-sperma . | ||
| 73 | docker 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 | |||
| 81 | Notes: | ||
| 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 | ||
| 61 | Slash commands are autoregistered globally on startup. Some are registered | 102 | Slash 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 @@ | |||
| 1 | services: | ||
| 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" | ||
