Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .daemonless/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ cit:
port: 8080
# Served with no auth middleware, so it answers before any user is created.
health: /health
ready: "Running at"
# Frontend (npm ci + vite) plus a full Go build from source.
timeout: 180
31 changes: 19 additions & 12 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,21 @@ RUN if [ -z "${APP_VERSION}" ]; then \
rm /tmp/src.tar.gz

# --- Frontend ---
# MUST run before `go build`: the backend go:embed's backend/http/embed/*.
# Use `npm run build` (NOT `build:docker`) — build:docker emits only
# backend/http/dist, which is the on-disk path, leaving embed/ empty.
# MUST run before `go build`: the backend go:embed's frontend assets.
# In v1.5.x, assets embed into backend/http/embed/*.
# In v2.0.x, assets embed into backend/internal/web/embed/* (e.g. public/index.html).
# Use `npm run build` (NOT `build:docker`) — build:docker emits only the
# on-disk dist path, leaving embed/ empty.
# `npm ci` against the committed lockfile, not upstream's `npm i`, which can
# silently update it.
WORKDIR /src/frontend
RUN npm ci --no-audit --no-fund
RUN mkdir -p /src/backend/http/embed && npm run build
RUN mkdir -p /src/backend/http/embed /src/backend/internal/web/embed && npm run build

# Drift guard: if upstream renames the embed target, `go build` would still
# succeed against a stale or empty embed dir and ship a blank UI.
RUN test -n "$(find /src/backend/http/embed -type f -name '*.html' -print -quit)" || \
{ echo "EMBED DRIFT: no HTML in backend/http/embed after 'npm run build'"; exit 1; }
RUN test -n "$(find /src/backend -type f -name '*.html' -path '*/embed/*' -print -quit)" || \
{ echo "EMBED DRIFT: no HTML in backend embed directory after 'npm run build'"; exit 1; }

# --- Backend ---
# CGO_ENABLED=0 is deliberate and load-bearing:
Expand All @@ -76,7 +78,11 @@ RUN test -n "$(find /src/backend/http/embed -type f -name '*.html' -print -quit)
# Result: a static binary, no C toolchain, no runtime shared-library deps.
WORKDIR /src/backend
ENV CGO_ENABLED=0
RUN VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/common/version" && \
RUN if [ -d /src/backend/internal/version ]; then \
VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/internal/version"; \
else \
VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/common/version"; \
fi && \
APP_VERSION=$(cat /tmp/app_version) && \
go build -trimpath \
-ldflags "-w -s -X '${VERSION_PKG}.Version=${APP_VERSION}'" \
Expand Down Expand Up @@ -142,11 +148,12 @@ RUN mkdir -m 0755 -p /app /config /srv && \
COPY --chmod=0755 --chown=bsd:bsd --from=builder /filebrowser-quantum /app/filebrowser-quantum
COPY --chmod=0644 --chown=bsd:bsd --from=builder /tmp/version /app/version

# NOTE: deliberately no /app/http/dist on disk.
# backend/cmd/root.go does os.Stat("http/dist") *relative to the process CWD* and
# prefers on-disk assets over the embedded FS when that path exists. Shipping the
# dist tree would make the UI depend on whatever directory s6 happens to exec
# from. The embedded FS has no such dependency, so the binary stands alone.
# NOTE: deliberately no dist directory on disk.
# backend/cmd/root.go checks relative to the process CWD (http/dist in v1.5,
# internal/web/dist in v2.0) and prefers on-disk assets over the embedded FS
# when that path exists. Shipping the dist tree would make the UI depend on
# whatever directory s6 happens to exec from. The embedded FS has no such
# dependency, so the binary stands alone.

COPY root/ /

Expand Down
31 changes: 19 additions & 12 deletions Containerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,21 @@ RUN if [ -z "${APP_VERSION}" ]; then \
rm /tmp/src.tar.gz

# --- Frontend ---
# MUST run before `go build`: the backend go:embed's backend/http/embed/*.
# Use `npm run build` (NOT `build:docker`) — build:docker emits only
# backend/http/dist, which is the on-disk path, leaving embed/ empty.
# MUST run before `go build`: the backend go:embed's frontend assets.
# In v1.5.x, assets embed into backend/http/embed/*.
# In v2.0.x, assets embed into backend/internal/web/embed/* (e.g. public/index.html).
# Use `npm run build` (NOT `build:docker`) — build:docker emits only the
# on-disk dist path, leaving embed/ empty.
# `npm ci` against the committed lockfile, not upstream's `npm i`, which can
# silently update it.
WORKDIR /src/frontend
RUN npm ci --no-audit --no-fund
RUN mkdir -p /src/backend/http/embed && npm run build
RUN mkdir -p /src/backend/http/embed /src/backend/internal/web/embed && npm run build

# Drift guard: if upstream renames the embed target, `go build` would still
# succeed against a stale or empty embed dir and ship a blank UI.
RUN test -n "$(find /src/backend/http/embed -type f -name '*.html' -print -quit)" || \
{ echo "EMBED DRIFT: no HTML in backend/http/embed after 'npm run build'"; exit 1; }
RUN test -n "$(find /src/backend -type f -name '*.html' -path '*/embed/*' -print -quit)" || \
{ echo "EMBED DRIFT: no HTML in backend embed directory after 'npm run build'"; exit 1; }

# --- Backend ---
# CGO_ENABLED=0 is deliberate and load-bearing:
Expand All @@ -70,7 +72,11 @@ RUN test -n "$(find /src/backend/http/embed -type f -name '*.html' -print -quit)
# Result: a static binary, no C toolchain, no runtime shared-library deps.
WORKDIR /src/backend
ENV CGO_ENABLED=0
RUN VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/common/version" && \
RUN if [ -d /src/backend/internal/version ]; then \
VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/internal/version"; \
else \
VERSION_PKG="github.com/gtsteffaniak/filebrowser/backend/common/version"; \
fi && \
APP_VERSION=$(cat /tmp/app_version) && \
go build -trimpath \
-ldflags "-w -s -X '${VERSION_PKG}.Version=${APP_VERSION}'" \
Expand Down Expand Up @@ -147,11 +153,12 @@ RUN mkdir -m 0755 -p /app /config /srv && \
COPY --chmod=0755 --chown=bsd:bsd --from=builder /filebrowser-quantum /app/filebrowser-quantum
COPY --chmod=0644 --chown=bsd:bsd --from=builder /tmp/version /app/version

# NOTE: deliberately no /app/http/dist on disk.
# backend/cmd/root.go does os.Stat("http/dist") *relative to the process CWD* and
# prefers on-disk assets over the embedded FS when that path exists. Shipping the
# dist tree would make the UI depend on whatever directory s6 happens to exec
# from. The embedded FS has no such dependency, so the binary stands alone.
# NOTE: deliberately no dist directory on disk.
# backend/cmd/root.go checks relative to the process CWD (http/dist in v1.5,
# internal/web/dist in v2.0) and prefers on-disk assets over the embedded FS
# when that path exists. Shipping the dist tree would make the UI depend on
# whatever directory s6 happens to exec from. The embedded FS has no such
# dependency, so the binary stands alone.

COPY root/ /

Expand Down
2 changes: 1 addition & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ services:
- TZ=UTC

volumes:
- /path/to/containers/filebrowser-quantum:/config
- /containers/filebrowser-quantum:/config
- /path/to/your/files:/srv

ports:
Expand Down
14 changes: 7 additions & 7 deletions root/etc/services.d/filebrowser-quantum/run
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ if [ ! -f "$CONFIG_FILE" ]; then
# 8080, not upstream's default of 80: this container runs the app as the
# unprivileged `bsd` user, and binding <1024 fails with
# "listen tcp 0.0.0.0:80: bind: permission denied".
server:
http:
port: 8080
baseURL: "/"
server:
cacheDir: "/config/cache"
sources:
- path: "/srv"
Expand All @@ -32,17 +33,16 @@ EOF
fi

export FILEBROWSER_CONFIG="$CONFIG_FILE"
export FILEBROWSER_DATABASE="${FILEBROWSER_DATABASE:-/config/database.db}"
export FILEBROWSER_DATABASE_PATH="${FILEBROWSER_DATABASE_PATH:-/config/filebrowser.sqlite}"

# The config file's parent dir holds the BoltDB, so it must be writable by bsd.
# The config file's parent dir holds the database, so it must be writable by bsd.
chown -R bsd:bsd /config 2>/dev/null || true

exec 2>&1

# `cd /app` is load-bearing, not tidiness: backend/cmd/root.go does
# os.Stat("http/dist") relative to the process CWD and serves from disk if it
# finds one. /app deliberately has no http/dist, so the embedded assets win and
# the UI cannot depend on where s6 happened to exec from.
# os.Stat("internal/web/dist") relative to the process CWD and serves from disk
# if it finds one. /app deliberately has no dist tree, so the embedded assets win.
s6-ready-when fetch -qo /dev/null "${HEALTHCHECK_URL:-http://localhost:8080/health}"

exec s6-setuidgid bsd cd /app /app/filebrowser-quantum
cd /app && exec s6-setuidgid bsd /app/filebrowser-quantum
Loading