Installation
Pick the path that matches how far you want to go:
| Path | What you get | Best for |
|---|---|---|
| Minimal | One container, SQLite, a local folder. No external services. | Trying it out, small/personal use |
| Full stack (Compose) | filex + PostgreSQL + Redis + OnlyOffice + an S3 server + Caddy (auto-HTTPS). | Real self-hosting, teams |
| Kubernetes (Helm) | Helm chart with toggles for the same components. | Clusters |
| App stores | Ready-made packages for Umbrel, CasaOS, Runtipi, Unraid, Portainer. | Home servers / NAS |
| Binary | A single static binary. | No Docker, systemd, edge devices |
All paths end at the same place: the app at …/admin (the operator's door) or at …/drive (the neutral door, for the accounts you hand out). Both open Home - your storages, what you opened last, what you starred - with the admin panel one click away from the explorer's header for whoever is allowed it, and both start with a first-run admin account (see First run).
Images. ghcr.io/brf-tech/filex:latest is the full-featured image (thumbnails for image/video/pdf/office included). …:slim is a smaller image without the thumbnail toolchain - thumbnails then degrade to placeholder cards. The binary inside both is identical; only the runtime tooling differs.
Minimal
The fastest way to a running instance - SQLite, embedded search + thumbnails, one local storage folder, nothing external.
docker run -d --name filex -p 5212:5212 \
-e FILEX_PUBLIC_URL=http://localhost:5212 \
-e FILEX_DEFAULT_STORAGE_DRIVER=local \
-e FILEX_DEFAULT_STORAGE_PATH=/srv/files \
-v filex-data:/data \
-v "$PWD:/srv/files" \
ghcr.io/brf-tech/filex:latestThe two FILEX_DEFAULT_STORAGE_* variables are what make the explorer show something. Without them filex boots with no storage at all and the UI says "No storage configured" - correct, and a poor first minute. With them, the folder you bind at /srv/files is seeded as a local storage named Files on first boot, so your files are there when you log in.
/dataand/srv/filesare different things and should not be the same directory:/datais filex's own state (SQLite database, search index, thumbnail cache, first-run secret),/srv/filesis content. A named volume for the first and a bind mount for the second keeps them apart.
Open http://localhost:5212/admin and grab the first-run password from the logs (docker logs filex).
Prefer Compose? Use deploy/compose/docker-compose.minimal.yml:
cd deploy/compose
docker compose -f docker-compose.minimal.yml up -d
docker compose -f docker-compose.minimal.yml logs -f # first-run credsIt mounts ./files into the container at /srv/files and seeds it as the default local storage, so anything you drop into deploy/compose/files shows up in the explorer with no setup at all. More drivers - S3, SFTP, WebDAV, FTP, SMB - are added from the admin UI (see STORAGE.md).
Set
FILEX_PUBLIC_URLto the URL people actually open. Behind a reverse proxy that's yourhttps://…domain - it's baked into share links, the OIDC redirect, and OnlyOffice callbacks.
Full stack (Docker Compose)
A production-shaped, one-command stack: filex + PostgreSQL (database) + Redis (queue) + OnlyOffice (document editing) + an S3 server (Versity S3 Gateway) + Caddy (reverse proxy with automatic HTTPS), and optionally ClamAV (antivirus - the filex image ships no scanner, so it runs as its own container under the clamav profile). Files in deploy/compose/.
1. Configure.
cd deploy/compose
cp .env.example .env
$EDITOR .env # set domains + secretsSet FILEX_DOMAIN / ONLYOFFICE_DOMAIN and strong secrets (openssl rand -hex 24). The ONLYOFFICE_JWT_SECRET must be identical between filex and OnlyOffice - the compose file already wires the same variable to both.
Zero-touch config. The admin account, SSO/LDAP/header auth, SMTP, branding and an initial storage can all be set through
FILEX_*env in.env, so the stack comes up already configured (no first-run UI clicks). See CONFIGURATION.md (Authentication + Zero-touch seeding) and thedeploy/compose/examples.
2. DNS. Point FILEX_DOMAIN and ONLYOFFICE_DOMAIN at the host. Open ports 80 and 443 so Caddy can issue certificates.
3. Launch.
docker compose -f docker-compose.full.yml --env-file .env up -d
docker compose -f docker-compose.full.yml logs -f filex # first-run credsCaddy fetches HTTPS certs automatically; open https://<FILEX_DOMAIN>/admin (your users: https://<FILEX_DOMAIN>/drive).
4. Wire the bundled S3 server as a storage (the versitygw profile). It makes the bucket FILEX_DEFAULT_STORAGE_S3_BUCKET names (default filex) on start; seed it as the default storage with Option A in .env, or Storages → Add an S3 storage:
- endpoint
http://versitygw:7070(filex reaches it over the internal network) path_style= true,region=us-east-1(part of the signature:autois refused)bucket=filex,prefix=filesaccess_key/secret_key= yourS3_ACCESS_KEY/S3_SECRET_KEY
What it is, and how to move off the MinIO an older stack bundled: STORAGE.md → A local S3 server.
OnlyOffice is already connected - open any Office file to edit it. See ONLYOFFICE.md and STORAGE.md.
Want a lighter "full"? Delete the services you don't need from the compose file and drop the matching
FILEX_*env from thefilexservice - everything is additive.
Kubernetes (Helm)
A Helm chart lives at deploy/helm/filex/. It deploys filex with a PVC for /data and an Ingress, and can pull in PostgreSQL, Redis, OnlyOffice and an S3 server (Versity S3 Gateway) as optional dependencies.
# Minimal: filex + SQLite + a local PVC.
helm install filex ./deploy/helm/filex \
--set ingress.host=files.example.com
# Full: enable the bundled dependencies.
helm install filex ./deploy/helm/filex -f deploy/helm/filex/values-full.yaml \
--set ingress.host=files.example.com \
--set onlyoffice.ingress.host=office.example.comKey values.yaml toggles: postgresql.enabled, redis.enabled, onlyoffice.enabled, versitygw.enabled, persistence.size, ingress.*, basePath, trustedProxies, resources. See the chart's values.yaml for the full list.
trustedProxies sets FILEX_TRUSTED_PROXIES, the proxies whose X-Forwarded-For filex believes. Empty, it is auto: in a pod, the pod's own interface subnet - a node's /24 with flannel, the pod alone (/32) with Calico or Cilium - so an ingress controller on another node is not trusted and every visitor would resolve to its address. List your cluster's pod network, keeping auto: --set trustedProxies="auto, 10.244.0.0/16" (or the ingress controller's range); a CDN in front of the ingress is listed too. The Sign-in security page names a peer that sends forwarded addresses without being trusted (CONFIGURATION.md → Trusted proxies).
To serve filex under a path of the host instead of its root (https://files.example.com/filex/), set basePath: the chart sets FILEX_BASE_PATH and routes that path on the Ingress, passing it through unchanged - don't add a rewrite/strip annotation (Serving filex under a sub-path):
helm install filex ./deploy/helm/filex \
--set ingress.host=files.example.com \
--set basePath=/filex \
--set publicURL=https://files.example.com/filexZero-touch config. Auth (OIDC/LDAP/header), the first admin, SMTP, branding and a default storage can all be supplied as
FILEX_*env in the chart values, so a fresh release boots fully configured with no admin-UI setup. See CONFIGURATION.md (Authentication + Zero-touch seeding) and thedeploy/helm/filex/values examples.
App stores
Ready-made packages for the common home-server platforms live under deploy/ - each directory has a README with install steps. All of them run the same single container (SQLite + local storage) and end at the same first run.
- Umbrel -
deploy/umbrel/filex/: community app-store package (app-proxy pattern); login isadmin@local+ the password Umbrel shows in the app dialog. - CasaOS -
deploy/casaos/: compose with thex-casaosstore metadata; paste it via Install a customized app. - Runtipi -
deploy/runtipi/filex/: dynamic compose app; the install form asks for the (optional) first admin. - Unraid -
deploy/unraid/: Community Applications template XML; map a share to/srv/filesand set the Public URL. - Portainer -
deploy/portainer/: App Templates v3 JSON; add the raw URL under Settings → App Templates.
Binary
filex ships as a single static binary (CGO-free) for linux/macOS/Windows × amd64/arm64. Download the archive for your platform from the Releases page, extract, then:
./filex serveConfiguration is via FILEX_* environment variables (see CONFIGURATION.md) or a config.yaml (filex serve --config /path/to/config.yaml, or the FILEX_CONFIG env). Data goes to ~/.filex by default (override with FILEX_DATA_DIR).
systemd (Linux):
# /etc/systemd/system/filex.service
[Unit]
Description=filex file manager
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=filex
ExecStart=/usr/local/bin/filex serve
Restart=on-failure
RestartSec=5s
Environment=FILEX_DATA_DIR=/var/lib/filex
Environment=FILEX_LISTEN=127.0.0.1:5212
Environment=FILEX_PUBLIC_URL=https://files.example.com
[Install]
WantedBy=multi-user.targetsudo useradd --system --home /var/lib/filex --create-home filex
sudo install -m 0755 filex /usr/local/bin/filex
sudo systemctl enable --now filexPut it behind a reverse proxy for TLS (see Behind a reverse proxy).
Thumbnail tools (optional). The binary draws image, SVG, text and archive thumbnails by itself. Video, audio, PDF and HEIC/AVIF previews need programs on PATH; on Debian or Ubuntu:
sudo apt install ffmpeg ghostscript poppler-utils \
imagemagick libheif-plugin-libde265Office documents are not a program here: their previews, and the apps' office conversions (the Convert app's Word/Excel/PowerPoint/OpenDocument targets), come from the ONLYOFFICE Document Server you connect under External services (ONLYOFFICE.md). Since 0.50 filex runs no LibreOffice, even one installed on this machine.
⚠ libheif-plugin-libde265 is the HEVC decoder a HEIC photo needs. Ubuntu 24.04's libheif1 only suggests it, so apt install imagemagick alone leaves ImageMagick listing HEIC and decoding none. filex checks at start by decoding a sample: About then shows HEIC photos: Not found and the photos are listed under Admin → Tools → Thumbnail repair with the package to install. Restart filex after installing any of these. See thumbnails.md.
Build from source
Requires Go 1.25+, Node 20+, pnpm 9+.
git clone https://github.com/BRF-Tech/filex.git
cd filex
pnpm install
pnpm run build:all # packages -> admin UI -> embed -> Go binary
./bin/filex serveCGO_ENABLED=0 means you can cross-compile without a C toolchain (GOOS=linux GOARCH=arm64 go build ./backend/cmd/filex).
First run
On the very first start (empty user table) filex creates an admin account and prints the password once:
─── First run detected ─────────────────────────────────────
Admin user created:
Email: admin@local
Password: <generated 16-char password>
Saved to: <data-dir>/.first-run.txt (mode 0600, shown ONCE)
Change at: /admin/dashboard?settings=1The account's username is admin, whatever its email: the SFTP, FTPS and sign-in forms accept either. admin is reserved - no other account can take it, and a later account whose email starts with admin@ becomes admin2. (Installs created before v0.43.0 keep the name their first administrator already has, usually admin2.)
That last line is a deep link, not a page: the account fields live in the user-settings dialog behind the avatar menu, and a dialog has no address of its own to print. Sign in at …/admin, open it, and change the password under Account. Lost the password? Reset from the CLI:
filex admin random-password --email admin@local
# in Docker: docker exec filex filex admin random-password --email admin@localEnable TOTP 2FA per user under user settings → Security. Add SSO/LDAP via SSO.md / CONFIGURATION.md. On a binary install, people can also sign in with the Windows or Linux account of the machine filex runs on (OS-LOGIN.md; the Linux one is not for Docker). Wrong passwords are limited from the first start - put your own address on the allow-list before you need it (sign-in attempt limits).
Behind a reverse proxy
filex serves everything from one origin on port 5212 - the SPA, the API, public share (/s/…) and file-drop (/d/…) pages, /embed.js, the realtime WebSocket at /api/ws, /healthz. Forward / to filex:5212 and:
- set
FILEX_PUBLIC_URLto the externalhttps://…URL, - pass the real client IP (
X-Real-IP/X-Forwarded-For) - used for audit, file-drop rate limiting and the sign-in attempt limit, and believed only from a trusted proxy (FILEX_TRUSTED_PROXIES; by defaultauto: this machine and, in a container, the other containers on its network - list a proxy on the host in front of the container, on another machine, or a CDN in front of your proxy - Trusted proxies), - allow large bodies (e.g.
5G) and long timeouts for uploads, - allow WebSocket/SSE upgrades -
/api/ws(the socket an open explorer runs on; blocked, it silently falls back to re-listing every 12 s) and the MCP stream at/api/ai/mcp- and pass the realHostheader, because the same-origin upgrade is origin-checked, - enable gzip for the admin SPA.
The bundled Caddy config (deploy/compose/Caddyfile) already does all of this; an nginx example is in DEPLOYMENT.md.
Under a path instead of a host of its own - https://example.com/filex/ - set FILEX_PUBLIC_URL=https://example.com/filex (its path becomes the base, or set FILEX_BASE_PATH=/filex) and have the proxy forward /filex/… with the path unchanged: Caddy handle, not handle_path. Caddy and nginx examples: DEPLOYMENT.md → Serving filex under a sub-path.
Data & backup
Everything filex owns lives under FILEX_DATA_DIR (/data in Docker):
instance.sqlite- the database (unless you use PostgreSQL/MySQL),search.bleve/- the full-text index (rebuildable),thumbs/- the thumbnail cache (regenerable; released when a file is purged, and swept for orphans everyFILEX_THUMBS_SWEEP_INTERVAL),cache/- the read cache for slow storages (disposable),uploads/- staging for chunked and resumable uploads. ⚠ Until a transfer commits, this is the file's only copy - it is not disposable while one is in flight,ssh/andftps/- the SFTP host keys and the FTPS self-signed certificate, generated on the first boot that enables those listeners. Regenerable, but regenerating them is a changed host key: every client that has connected before refuses the next connection until somebody clears it,plugins/,dav/- installed storage-plugin binaries and the WebDAV lock store,.first-run.txt- the initial admin secret.
Back up the database (the SQLite file, or your Postgres), your storage backends, and ⚠⚠ FILEX_SECRET_KEY if you set one - it seals the S3 access keys, so a restored database without the matching key has access keys that no longer verify. See Backup & restore. The search index and thumbnail cache can be rebuilt (POST /api/admin/search/rebuild, filex thumb backfill), so backing them up is optional.
Upgrading
Pull the new image (or binary) and restart - migrations run automatically on startup. Back up the database first. To roll a schema back one step manually: filex migrate down.
⚠ A Compose stack or Helm release that bundled MinIO (before 0.50): the bundled S3 server is now Versity S3 Gateway, and MinIO's data has to be copied across. Nothing is deleted for you, and the chart refuses to render until you have kept the old volume - follow STORAGE.md → Moving off the bundled MinIO before you upgrade.
See also
- CONFIGURATION.md - every
FILEX_*variable - STORAGE.md · SSO.md · ONLYOFFICE.md
- DEPLOYMENT.md - reverse proxy, HTTPS, scaling, backup
