Run as PUID:PGID with a configurable umask

The image now starts through docker-entrypoint.sh: as root it hands
/data and the backup folder to PUID:PGID (only files owned by someone
else, so restarts stay fast), applies UMASK, and runs Canon as that
user with su-exec. Started with --user, it runs Canon directly. The
image defaults to 1000:1000; the Compose file presets Unraid's 99:100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-10-08 14:02:43 -07:00
1 parent c8afcdab3d
commit ec0b053eb3
6 files changed
+58 -6

No files matched your search

+1
View File
@@ -5,6 +5,7 @@
# Build tools may add their own copy of the Dockerfile (Arcane writes
# .arcane.external.Dockerfile), which has to reach the builder.
!*Dockerfile*
!docker-entrypoint.sh
!go.mod
!go.sum
!cmd/
+5
View File
@@ -31,6 +31,11 @@ CANON_IGDB_CLIENT_SECRET=
# CANON_DATA_PATH=/srv/canon
# Where backups are written. Default: the canon_canon-backups volume.
# CANON_BACKUP_PATH=/mnt/nas/canon-backups
# Owner of the files Canon writes (database, images, backups). The Compose
# file defaults to Unraid's 99:100 (nobody:users).
# PUID=99
# PGID=100
# UMASK=022
# Host address and port to publish on.
# CANON_BIND=0.0.0.0
# CANON_PUBLISH_PORT=4242
+11 -6
View File
@@ -7,15 +7,20 @@ ARG VERSION=dev
RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o /out/canon ./cmd/canon
FROM alpine:3
RUN apk add --no-cache ca-certificates tzdata \
&& adduser -D -u 1000 canon \
&& mkdir -p /data /backups && chown canon:canon /data /backups
RUN apk add --no-cache ca-certificates tzdata su-exec \
&& mkdir -p /data /backups
COPY --from=build /out/canon /usr/local/bin/canon
USER canon
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh
# The entrypoint starts as root only to set ownership, then runs Canon as
# PUID:PGID.
ENV CANON_DATA_DIR=/data \
CANON_PORT=4242
CANON_PORT=4242 \
PUID=1000 \
PGID=1000 \
UMASK=022
VOLUME /data /backups
EXPOSE 4242
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -qO- http://127.0.0.1:${CANON_PORT}/healthz >/dev/null || exit 1
ENTRYPOINT ["canon"]
ENTRYPOINT ["docker-entrypoint.sh"]
+2
View File
@@ -87,6 +87,8 @@ Keys entered in the UI are stored in the SQLite database on your server. Keys se
| `CANON_HOST` | `0.0.0.0` | Interface to listen on. `0.0.0.0` makes it reachable from other devices on your network; `127.0.0.1` keeps it on this machine |
| `CANON_PORT` | `4242` | Listen port |
| `CANON_DATA_DIR` | `/data` (Docker), `./data` | Database + cached images |
| `PUID` / `PGID` | `1000` (image), `99`/`100` (Compose) | Docker only: the user and group that own everything Canon writes. The Compose file defaults to Unraid's `nobody:users` |
| `UMASK` | `022` | Docker only: permissions for new files (`002` makes them group-writable) |
| `CANON_ADMIN_USERNAME` / `_PASSWORD` | | Create the owner account on first start |
| `CANON_TMDB_API_KEY` | | |
| `CANON_TVDB_API_KEY` / `CANON_TVDB_PIN` | | The PIN is only for user-supported keys |
+5
View File
@@ -28,6 +28,11 @@ services:
- ${CANON_BACKUP_PATH:-canon-backups}:/backups
environment:
CANON_BACKUP_DIR: /backups
# Who owns the files Canon writes. 99:100 is Unraid's nobody:users;
# use 1000:1000 (or your own `id -u`:`id -g`) elsewhere.
PUID: ${PUID:-99}
PGID: ${PGID:-100}
UMASK: ${UMASK:-022}
env_file:
- path: .env
required: false
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
# Runs Canon as PUID:PGID (default 1000:1000) so files it creates on mounted
# folders belong to that user, e.g. 99:100 (nobody:users) on Unraid.
set -eu
PUID="${PUID:-1000}"
PGID="${PGID:-1000}"
UMASK="${UMASK:-022}"
case "$PUID$PGID" in
*[!0-9]*) echo "canon: PUID and PGID must be numbers (got PUID=$PUID PGID=$PGID)" >&2; exit 1 ;;
esac
case "$UMASK" in
*[!0-7]*) echo "canon: UMASK must be octal, like 022 or 002 (got $UMASK)" >&2; exit 1 ;;
esac
umask "$UMASK"
# Started with `user:` set in Compose or `--user`: nothing to switch.
if [ "$(id -u)" != 0 ]; then
exec canon "$@"
fi
data="${CANON_DATA_DIR:-/data}"
backups="${CANON_BACKUP_DIR:-$data/backups}"
for dir in "$data" "$backups"; do
mkdir -p "$dir"
# Only touch what's owned by someone else, so restarts stay fast.
if ! find "$dir" \( ! -user "$PUID" -o ! -group "$PGID" \) -exec chown "$PUID:$PGID" {} + 2>/dev/null; then
echo "canon: couldn't give $PUID:$PGID ownership of everything in $dir (a network share?); continuing" >&2
fi
done
export HOME="$data"
exec su-exec "$PUID:$PGID" canon "$@"