File Browser in Depth: What It Is and How to Run It on Kubernetes

File Browser is a lightweight, self-hosted web file manager: upload, download, preview, share links, and manage users—all through a browser, backed by a directory on disk. This guide explains how it works under the hood, when it fits (and when it does not), how to configure it safely, and how to deploy it on Kubernetes with persistent storage, secrets, Ingress, and production-minded hardening.

In short

File Browser serves files from a root path on a volume and stores users, permissions, and share metadata in a SQLite database. On Kubernetes: mount a PVC for /srv (files) and /config (database), bootstrap admin credentials with an init container, expose a Deployment + Service, front it with Ingress + TLS, and treat it like any privileged admin surface—strong auth, network restrictions, and backups.

What File Browser is (and is not)

File Browser (often written filebrowser, project repo filebrowser/filebrowser) is a single Go binary with a built-in web UI. You point it at a filesystem directory—the root—and it becomes a multi-user file portal with role-based rules, public share links, and basic file operations (rename, move, zip, preview for common types).

It is not a replacement for object storage (S3, MinIO), a sync client (Nextcloud, Syncthing), or an enterprise document platform. It is a pragmatic choice when you need a small team to browse and exchange files on a server, NAS export, or PVC-backed folder without building a custom UI.

Good fit Poor fit
Internal file drop for ops, ML datasets on a shared volume, homelab media Internet-facing anonymous uploads without hardening
Quick UI on top of an NFS/EFS mount already used by batch jobs Fine-grained compliance workflows (legal hold, DLP, audit trails beyond basics)
Teaching Kubernetes storage + Ingress with a real app Millions of small files or heavy concurrent writes (SQLite + single replica limits)

For Kubernetes fundamentals—Pods, Services, storage—see Kubernetes architecture in simple terms, hands-on Part 3 (workloads), and PV, PVC, and StorageClass.

Architecture: three pieces that matter

Operationally, File Browser is three concerns wired together:

  1. File root — The directory users see (e.g. /srv). Everything under this tree is reachable according to user rules.
  2. SQLite database — Default filebrowser.db holds users, password hashes, share links, and per-path permission rules. Losing this file loses accounts and shares (not necessarily the files themselves).
  3. HTTP server — Serves the SPA UI and JSON API on a TCP port (default 80 in the official container image).

The process is typically started with flags such as --root, --database, and --port. Configuration can also be prepared offline with the CLI (filebrowser config init, filebrowser users add) before the server starts—this pattern maps cleanly to Kubernetes init containers.

Request path (mental model)

Browser → Ingress (TLS) → Service → Pod:8080 → File Browser
                                              ├─ reads/writes /srv (PVC)
                                              └─ reads/writes /config/filebrowser.db (PVC)

Authentication is session-based in the app layer. Kubernetes RBAC governs who can change manifests; it does not replace File Browser login. For defense in depth, combine both.

Configuration deep dive

CLI and database bootstrap

On first install you initialize config and create an admin user against the database path you will use in production:

# Paths match the Kubernetes mounts in the manifests below
filebrowser config init \
  --database=/config/filebrowser.db

filebrowser users add admin "$FB_ADMIN_PASSWORD" \
  --database=/config/filebrowser.db \
  --perm.admin

Additional users, per-directory scopes, and “execute” permissions are managed with filebrowser users and filebrowser rules. Prefer scoped non-admin users for day-to-day access; reserve admin for break-glass.

Runtime flags (container entrypoint)

Common production-oriented flags:

  • --root=/srv — File tree exposed in the UI.
  • --database=/config/filebrowser.db — Keep the DB on persistent disk, not the container layer.
  • --port=8080 — Listen on a non-privileged port inside the pod (matches probes and Service targetPort).
  • --address=0.0.0.0 — Required in containers so the process accepts traffic from the cluster network.

Avoid --noauth outside local labs. Anonymous admin UIs on cluster networks have led to many incident reports across similar tools.

Branding and settings

Advanced installs mount a settings.json (branding, signup toggles, command execution). Treat that file like code: store in a ConfigMap only if it contains no secrets; keep credentials in Secret objects and inject via env vars referenced by init containers.

Kubernetes setup: end-to-end manifests

The following example deploys File Browser into namespace filebrowser with one replica, two PVCs (files + config), an init container to create the admin user once, ClusterIP Service, and Ingress. Adjust storage class, hostnames, and resource sizes for your cluster.

0 — Prerequisites

  • A working cluster and kubectl context (local lab: hands-on Part 1).
  • A StorageClass for dynamic PVCs (storage guide).
  • An Ingress controller (nginx, Traefik, etc.) and optionally cert-manager for TLS.

1 — Namespace and secrets

apiVersion: v1
kind: Namespace
metadata:
  name: filebrowser
---
apiVersion: v1
kind: Secret
metadata:
  name: filebrowser-admin
  namespace: filebrowser
type: Opaque
stringData:
  FB_ADMIN_PASSWORD: "change-me-before-apply"

Generate a strong password (openssl rand -base64 24) and prefer sealing or external secrets in production rather than committing plaintext.

2 — PersistentVolumeClaims

Splitting data and config lets you resize, snapshot, or restore them independently. For NFS/EFS (ReadWriteMany), you can run more than one replica only if the app and database semantics allow it—File Browser’s embedded SQLite expects one writer; use replicas: 1 unless you externalize the database.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: filebrowser-files
  namespace: filebrowser
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  storageClassName: standard   # replace with your class
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: filebrowser-config
  namespace: filebrowser
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard

3 — Deployment with init bootstrap

The init container uses the same image as the app and only runs CLI commands. The main container mounts both PVCs read/write, runs as non-root where possible, and defines probes on the HTTP port.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: filebrowser
  namespace: filebrowser
  labels:
    app.kubernetes.io/name: filebrowser
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: filebrowser
  template:
    metadata:
      labels:
        app.kubernetes.io/name: filebrowser
    spec:
      securityContext:
        fsGroup: 1000
      initContainers:
        - name: bootstrap
          image: filebrowser/filebrowser:v2.27.0
          imagePullPolicy: IfNotPresent
          env:
            - name: FB_ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: filebrowser-admin
                  key: FB_ADMIN_PASSWORD
          command:
            - /bin/sh
            - -c
            - |
              set -e
              if [ ! -f /config/filebrowser.db ]; then
                filebrowser config init --database=/config/filebrowser.db
                filebrowser users add admin "$FB_ADMIN_PASSWORD" \
                  --database=/config/filebrowser.db \
                  --perm.admin
              fi
          volumeMounts:
            - name: config
              mountPath: /config
      containers:
        - name: filebrowser
          image: filebrowser/filebrowser:v2.27.0
          imagePullPolicy: IfNotPresent
          args:
            - --root=/srv
            - --database=/config/filebrowser.db
            - --port=8080
            - --address=0.0.0.0
          ports:
            - name: http
              containerPort: 8080
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 3
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 20
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 500m
              memory: 256Mi
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: files
              mountPath: /srv
            - name: config
              mountPath: /config
      volumes:
        - name: files
          persistentVolumeClaim:
            claimName: filebrowser-files
        - name: config
          persistentVolumeClaim:
            claimName: filebrowser-config

Permission tip: If the pod stays CrashLoopBackOff with “permission denied” on EFS/NFS, check UID/GID alignment (fsGroup, storage mount options) or consult provider docs—this is a common class of issues when mounting shared filesystems. See also the Kubernetes troubleshooting playbook.

4 — Service

apiVersion: v1
kind: Service
metadata:
  name: filebrowser
  namespace: filebrowser
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: filebrowser
  ports:
    - name: http
      port: 80
      targetPort: http

For local testing without Ingress: kubectl port-forward -n filebrowser svc/filebrowser 8080:80 then open http://127.0.0.1:8080.

5 — Ingress (TLS)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: filebrowser
  namespace: filebrowser
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - files.example.com
      secretName: filebrowser-tls
  rules:
    - host: files.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: filebrowser
                port:
                  number: 80

Replace files.example.com, ingressClassName, and issuer annotations to match your platform. Restrict source IPs or place an OAuth2 proxy in front if the UI is reachable outside a trusted network.

Apply and verify

kubectl apply -f filebrowser/
kubectl get pods,svc,pvc -n filebrowser
kubectl logs -n filebrowser deploy/filebrowser -c bootstrap
kubectl logs -n filebrowser deploy/filebrowser -c filebrowser --tail=50

Expected flow: PVCs bound → init container creates DB once → main container listens on 8080 → Ingress routes HTTPS to the Service. Log in with user admin and the password from the Secret; change it in the UI or rotate the Secret and re-bootstrap only with a clear recovery plan.

Helm and GitOps alternatives

There is no official Helm chart merged in the upstream repo at the time of writing; community charts exist (for example third-party “filebrowser-chart” repositories). If you already standardize on Helm or GitOps, wrap the same primitives—PVC, Secret, Deployment, Service, Ingress—in a chart and pin image digests. The important part is not the packaging tool but the contract: persistent config, single replica with SQLite, version-pinned image, and sealed secrets.

Security hardening checklist

  • Authentication — Never expose --noauth on a network you do not fully trust.
  • TLS everywhere — Terminate HTTPS at Ingress; consider HSTS for public hostnames.
  • NetworkPolicy — Allow Ingress controller → Pod only; deny east-west traffic you do not need.
  • Least privilege users — Scoped folders per team; disable command execution unless required.
  • Resource limits — Cap CPU/memory to avoid noisy-neighbor impact on shared nodes (day-one practices).
  • Audit and backups — Snapshot PVCs or sync /srv to object storage; export filebrowser.db on a schedule.
  • Upgrades — Read release notes; test image bumps in a staging namespace before rolling production.

File Browser is effectively a remote admin surface over files. Treat it with the same seriousness as kubectl exec access to a node.

Operations: backup, upgrade, troubleshoot

Backup

  • Files — Volume snapshots (CSI), restic, or rsync from /srv.
  • Database — Copy filebrowser.db while quiesced or use brief maintenance windows; SQLite is sensitive to partial copies under write load.

Upgrade image tag

kubectl set image deployment/filebrowser -n filebrowser \
  filebrowser=filebrowser/filebrowser:v2.27.0
kubectl rollout status deployment/filebrowser -n filebrowser

Prefer Git-managed manifest bumps over imperative sets when practicing GitOps.

Common failures

Symptom Likely cause What to check
Pod Pending PVC not bound kubectl describe pvc -n filebrowser, StorageClass, quota
CrashLoopBackOff on start Volume permissions Pod events, fsGroup, mount UID on NFS/EFS
502 from Ingress Service has no endpoints Label selectors, readiness probe failing
Login fails after redeploy Ephemeral DB lost Confirm /config PVC mounted; init only ran on empty DB

When to choose something else

If you need S3 APIs, versioning, and IAM-style policies, use object storage and a dedicated client. If you need sync, calendar, and collaboration, use a full collaboration suite. File Browser shines when the requirement is simply: “give me a secure browser UI on top of this directory in the cluster.”

Further reading

Blog index · Kubernetes hands-on lab · K8s storage

K8s hands-on Storage guide