Skip to content

FileBrowser Quantum

Github GitHub


Overview

FileBrowser Quantum is an actively developed fork of the original File Browser: a web UI to browse, upload, download, preview and share files in a few directories. I use it for one thing: getting at a couple of NAS folders from a browser on a machine where I can't install anything (a work computer), without SMB or a VPN client.

Design rules that came out of an earlier, looser setup:

  • Mount only what you need, never the whole pool. Two sources: a Shared folder just for passing files around, and Documents, which is the Syncthing copy of my desktop folders (edits there sync back to the desktop).
  • Run as the files' owner (same UID as Syncthing), so ownership stays consistent, with a read-only root filesystem and no capabilities.
  • Two logins on the public path: SSO at the edge (Cloudflare Access in my case), then FileBrowser's own password with mandatory TOTP.
  • Config in git, mounted read-only. Users, shares and UI settings live in FileBrowser's SQLite DB, so it never needs to write the config file.

Compose

compose.yaml
services:
  filebrowser:
    # 2.0.x is the stable line. Full image (not -slim) for PDF/office/video previews.
    image: gtstef/filebrowser:2.0.1-stable
    container_name: filebrowser
    restart: unless-stopped
    user: "<PUID>:<PGID>"
    environment:
      - FILEBROWSER_CONFIG=/home/filebrowser/config/config.yaml
      - FILEBROWSER_DATABASE_PATH=/home/filebrowser/data/database.sqlite
      - FILEBROWSER_ADMIN_PASSWORD=${FILEBROWSER_ADMIN_PASSWORD}
      - FILEBROWSER_TOTP_SECRET=${FILEBROWSER_TOTP_SECRET}
      - TZ=<TZ>
    volumes:
      - ./config.yaml:/home/filebrowser/config/config.yaml:ro
      - /mnt/appdata/filebrowser/data:/home/filebrowser/data
      - /mnt/appdata/filebrowser/cache:/home/filebrowser/cache
      - /mnt/storage/filebrowser/shared:/srv/shared
      - /mnt/storage/sync:/srv/documents
    ports:
      # LAN address only; the reverse proxy forwards to it
      - "<YOUR_SERVER_IP>:89:80"
    read_only: true
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    mem_limit: 1g
    # Same command as the image's own HEALTHCHECK (curl is in the image)
    healthcheck:
      test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:80/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
.env.example
FILEBROWSER_ADMIN_PASSWORD=<YOUR_PASSWORD>   # initial admin password (12+ characters)
FILEBROWSER_TOTP_SECRET=<YOUR_TOKEN>         # encrypts users' TOTP seeds in the DB: openssl rand -hex 32

The cache directory (thumbnails/previews) can be excluded from backups; the data directory (SQLite DB) can't.

config.yaml

config.yaml
http:
  port: 80
  baseURL: "/"
  externalUrl: "https://files.<YOUR_DOMAIN>"
  # The reverse proxy sets X-Real-IP / X-Forwarded-For, so the login rate limit and lockout see the client
  trustProxyHeaders: true
  # Not used; one less authenticated surface
  disableWebDAV: true

server:
  # Renovate tracks the image tag
  disableUpdateCheck: true
  cacheDir: "/home/filebrowser/cache"
  cacheDirCleanup: true
  logging:
    - levels: "info|warning|error"
  sources:
    - path: "/srv/shared"
      name: "Shared"
      config:
        defaultEnabled: true
    - path: "/srv/documents"
      name: "Documents"
      config:
        defaultEnabled: true
        # No public share links out of the Syncthing folders
        private: true
        rules:
          # Hide Syncthing internals: version history, folder markers, temp files
          - folderName: ".stversions"
          - folderName: ".stfolder"
          - fileStartsWith: ".syncthing."
          - fileName: ".stignore"

auth:
  adminUsername: "<YOUR_USER>"
  # Web session length; the SSO gateway has its own session in front of this
  tokenExpirationHours: 8
  methods:
    password:
      enabled: true
      minLength: 12
      signup: false
      # Every password user must enroll TOTP at first login
      enforcedOtp: true
    passkey:
      enabled: false

frontend:
  name: "files"

Notes on the choices:

  • trustProxyHeaders only belongs on an instance that is reachable only through your proxy (here: the port is bound to the LAN address and the proxy is the only thing that uses it). Otherwise clients could fake their IP.
  • private: true on the Syncthing source blocks public share links for anything in there. Shared allows them, since handing a file to someone is its whole purpose.
  • The Syncthing rules keep .stversions and friends out of the UI; nobody should edit those by hand.

Sources & further reading