SSD Nodes Learn 🎉 VPS from $5.50/mo
Guides Matt ConnorBy Matt Connor

Self-hosted file managers compared

FileBrowser, Filestash, SFTPGo and Cloud Commander compared on scoping, share links, storage backends and auth, plus how to run one without risking your files.

What a self-hosted file manager is, and what it is not

A self-hosted file manager is a web page over the directory tree that already exists on your VPS (virtual private server). You log in, you see /srv/files exactly as it sits on disk, and you upload, rename, download or hand someone a link. Nothing is copied into a second system, so a file you drop in the browser is a file that ls shows one second later.

Search results blur this together with software that does other jobs. Sync tools keep a copy of every file on every device, which is what a self-hosted Dropbox alternative is for. Object storage has no directory tree at all, it has buckets and an API, so running MinIO for S3 compatible object storage answers a different question. Server admin panels manage the machine rather than the files, and that is the Cockpit and Webmin comparison instead.

You want a file manager when a colleague needs one 300 MB archive off the server, or when you want to fix a typo in a config file from a phone. The job is small, and so are the tools.

Keep one fact in view while you read. This is a web application with read and write access to your filesystem, listening on a port. Every choice below is really a choice about how much of your disk that process can reach.

FileBrowser is archived, so read this before you install it

FileBrowser, the filebrowser/filebrowser project, is the answer most guides still give. Its README now opens with a notice:

File Browser is archived on 2026-09-01. The last planned release has already shipped. There will be no further releases, bug fixes, or security fixes.

The Apache 2.0 code keeps working. The security fixes stop. That matters more for this category than for most, because the software's whole purpose is write access to a filesystem over HTTP.

The maintainers wrote down how to keep running it, and the advice is worth following whichever tool you pick: do not expose it directly to the internet, put it behind a reverse proxy that terminates TLS (transport layer security) and performs its own authentication, keep the command runner disabled, and run it unprivileged in a container with only the directory you intend to serve mounted into it.

One line in that README matters more than the rest. Sessions are self-contained JWTs (JSON web tokens) rather than server-side identifiers, so they cannot be revoked. A leaked session token stays valid until it expires, and changing the password does not kill it. If you keep FileBrowser, the authentication layer in front of it is doing the real work.

FileBrowser Quantum: the fork that is still moving

Active development moved to a fork, FileBrowser Quantum (gtsteffaniak/filebrowser), published as the gtstef/filebrowser image. It rebuilds configuration around a single config.yaml instead of the older mix of command line flags and database settings. The documented quick try:

docker run -d \
  -v $(pwd):/srv \
  -p 80:80 \
  gtstef/filebrowser:beta

That serves the current directory at http://localhost, and the first login is admin / admin. Change it before the container is reachable from anywhere except your own machine.

For an instance you keep, use Compose, mount the data directory rather than a single database file, and bind the port to localhost:

services:
  filebrowser:
    image: gtstef/filebrowser:beta
    user: "1000:1000"
    volumes:
      - /srv/files:/folder
      - ./data:/home/filebrowser/data
    ports:
      - 127.0.0.1:8080:80
    restart: unless-stopped

The config lives at /home/filebrowser/data/config.yaml and the database at /home/filebrowser/data/filebrowser.sqlite. Version 2.0.0 changed the database format and performs a one time migration, which is why the docs ask for a directory mount: a single file mount gives the migration nowhere to put the new file. The paths inside config.yaml are container paths, so a source in the config reads /folder, not /srv/files. Getting that backwards produces an empty file list with no error, because the directory genuinely is not there.

The project publishes latest and stable at around 60 MB with FFmpeg bundled for video thumbnails, and stable-slim at around 15 MB with the core only. Those are the figures on the install page in August 2026. Pin the tag you chose. latest changes without telling you, and a file manager that changes its config format under a running container is a bad morning.

For this job it is the strongest of the small tools. It serves multiple sources with include and exclude rules, so one instance can expose /srv/media and /srv/docs with different scope. Shares carry an expiration time and can be anonymous or restricted to a user. Authentication covers OIDC (OpenID Connect), LDAP (lightweight directory access protocol), password with two factor, and a proxy header mode. That proxy mode is what lets it sit behind single sign on (SSO) from a self-hosted Authentik server instead of keeping a second list of users.

Filestash: one interface over storage you already have

Filestash is a different shape. It is a front end that connects to a backend, and the backend list is long: FTP, SFTP (SSH file transfer protocol), S3, SMB, WebDAV, IPFS and about twenty more. It fits when the files are not on the box running the interface.

mkdir -p /srv/filestash && cd /srv/filestash
curl -O https://downloads.filestash.app/latest/docker-compose.yml
docker compose up -d

The image is machines/filestash:latest. Open http://your_domain:8334 and the first screen sets the admin password. Set it immediately, because until you do, the admin console is open to anyone who finds the port.

Understand the identity model before you build on it. Filestash does not keep a user database in the usual sense. Credentials are stored in your browser via cookies that are encrypted, authenticated and HTTP only, and nothing is kept server side unless you use the share feature, in which case Filestash keeps a persistent encrypted version of your credentials. The "users" are storage accounts: identity lives in the backend, in the SFTP account or the S3 key, not in Filestash.

That design is clean, and it has a price tag. The pricing page lists the free self-hosted tier as AGPL v3 (GNU Affero General Public License) with up to 3 users, and puts SSO (SAML, OIDC and LDAP) with role based access control in the paid self-hosted tier from $50 per month, as of August 2026. If the plan was "Filestash in front of company SSO, for free", check that page before you design around it.

SFTPGo: a protocol server that also has a web interface

SFTPGo is the most capable software here, and the one most often recommended for the wrong reason. It serves SFTP, HTTP/S, FTP/S and WebDAV, over local filesystem, encrypted local filesystem, S3 compatible object storage, Google Cloud Storage, Azure Blob Storage, or another SFTP server.

Binaries, Debian and Ubuntu packages and a container image are all published, and the current APT repository line and its signing key are on the installation page in the SFTPGo documentation. The container route is the fastest way to have it running, replacing tag with the version you want:

docker run --name some-sftpgo -p 8080:8080 -p 2022:2022 -d "drakkan/sftpgo:tag"

SFTP listens on 2022 and the web interfaces on 8080. Mount /srv/sftpgo as a volume or the accounts and their files vanish when the container is recreated, since user home directories default to /srv/sftpgo/data/<username>.

There are two web interfaces, and the difference is the part most write ups leave implicit. WebAdmin, at /web/admin, is administrative: it is where you create users, groups, virtual folders and event rules, and set quotas, bandwidth limits and access time restrictions. WebClient, at /web/client, is the end user view, where a person browses files, changes their own credentials, sets up two factor authentication and creates shares.

Those shares are the best in this comparison. A user can create HTTP/S links to share files and folders, limit the number of downloads and uploads, protect the share with a password, limit access by source IP address, and set an automatic expiration date.

So why the caution? The centre of gravity is the account model and the protocol server, not the browsing experience. Pick SFTPGo when other people need real accounts with quotas, when uploads arrive over SFTP or FTPS from a system you do not control, or when one bucket must appear inside several users' home directories. Virtual folders do that last one: a folder backed by local disk, S3, GCS, Azure Blob, SFTP or HTTP, mounted into multiple accounts, with a separate quota per user on a shared folder. If all you wanted was a browsable page over /srv/files, this is a large amount of machinery for it.

Two more facts worth having. The Community edition is AGPL-3.0-only with additional terms, alongside a commercially licensed Enterprise edition. OIDC login is in the open source build, and it maps identity provider users onto SFTPGo admins and users for both web interfaces. You can also switch the client interface off globally with enable_web_client in the httpd configuration, or per user by adding HTTP to that user's denied protocols, so the file manager can exist for one person and not for everybody.

Cloud Commander: two panes and a terminal, for one person

Cloud Commander is an MIT licensed Node.js manager in the two pane style, with a built in editor, a console and a terminal. Install it globally with npm i cloudcmd -g, or run the published container:

docker run -it --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd

Read that command before you run it. -v /:/mnt/fs mounts the entire host filesystem into the container, and the sample ~/.cloudcmd.json ships "root": "/", "auth": false and "console": true. That combination means anyone who reaches port 8000 gets your whole disk and a command console on the server. It is a sensible default for a laptop and a bad one for a VPS.

Scope it down. The container reads /root/.cloudcmd.json, which the published command supplies by mounting your home directory, so keep the config mount and drop the rest:

docker run -d --name cloudcmd \
  -v ~/.cloudcmd.json:/root/.cloudcmd.json \
  -v /srv/files:/srv/files \
  -w=/srv/files \
  -p 127.0.0.1:8000:8000 \
  coderaiser/cloudcmd

In that config file set "root" to /srv/files, "auth" to true with a "username" and "password", and "console" and "terminal" to false unless you truly want browser shell access. Command line equivalents exist as well, including --root, --auth, --username, --password and --prefix.

Be honest about what it is. One credential pair, no per user scoping, no quotas, no share links. It is a personal tool, so bind it to localhost as above and reach it through a tunnel:

ssh -L 8000:127.0.0.1:8000 you@your-vps

Then open http://127.0.0.1:8000 on your own machine. The file manager is never exposed publicly, and the only thing facing the internet is the SSH daemon you already hardened on your VPS.

Why Nextcloud is the wrong tool for this one job

Nextcloud is good software, and it is not this. It is a collaboration platform: a PHP application, a database, background jobs, desktop sync clients and an app store. Running it to get a web view of /srv/files is a lot of moving parts for a small job, and there is a specific mismatch. Nextcloud keeps file metadata in a database table instead of reading the directory on each request, so files written by rsync or a cron job can stay invisible in the interface until a scan catches up, with sudo -u www-data php occ files:scan --all. A file manager lists the directory when you load the page, so that gap does not exist.

Keep Nextcloud for what it does well: calendars, contacts, sync and sharing with people who expect a desktop client. Nextcloud on a VPS with Docker, TLS and backups covers that setup. If you already run it and only need to see an existing directory, enable the External Storage app and stop there. A second web application with write access to the same disk is a second thing to patch.

How to run one without giving away the whole server

Never point it at /. The process can read and write everything its user account can reach, so a stolen session token becomes exactly that much filesystem access. Serve one directory, /srv/files, and create it for the purpose.

Run it as a non-root user and mount only what it serves. In Compose that is user: "1000:1000" plus one bind mount per directory, with :ro on anything it never needs to write:

    volumes:
      - /srv/files:/folder
      - /srv/media:/media:ro

The usual result of that change is that browsing works and uploads fail with permission denied, because the user id inside the container does not own the directory outside it. Compare the two: docker exec filebrowser id prints the container user, ls -ln /srv/files prints the numeric owner on the host. Fix it with sudo chown -R 1000:1000 /srv/files. This is the same ownership problem that PUID and PGID in Docker images exist to solve.

Bind the published port to localhost, 127.0.0.1:8080:80 rather than 8080:80. Docker writes its own netfilter rules ahead of ufw's, so a plainly published port stays reachable from the internet even when ufw deny 8080 is active. Put a reverse proxy in front for TLS. Over plain HTTP the session cookie crosses the network in the clear, and that cookie is filesystem access. If Compose itself is new to you, Docker Compose on a VPS covers the file layout these snippets assume.

Add an authentication layer when the application's own is thin. HTTP basic auth at the proxy is enough for a single user instance. Once more than one person is involved, use OIDC or forward auth against an identity provider so that revoking one account revokes access everywhere.

Turn off the extras. Any file manager that offers a shell, a command runner or an in browser terminal is offering remote code execution to whoever holds a valid session. FileBrowser's own guidance is to keep the command runner disabled, and Cloud Commander's sample config enables the console. Decide on purpose rather than by default.

What breaks first, and the errors you will see

listen tcp :80: bind: permission denied. Linux reserves ports below 1024 for privileged processes. FileBrowser Quantum's documented config uses port 80, which is fine inside a container and fails the moment you run the binary as an unprivileged user on the host. Set a port above 1024 in config.yaml and let the proxy own 443.

Uploads fail while browsing works. Listing a directory needs r-x, writing into it needs w. The web interface reports a generic error, so check the filesystem instead of the application logs first.

413 Request Entity Too Large. That error comes from nginx, not from the file manager. The default client_max_body_size is 1 MB, so a larger upload is rejected at the proxy before the application ever sees it. Set client_max_body_size 4096m; in the server block, or 0 to disable the check.

Uploaded files carry the wrong group. New files are owned by the process user, whatever the surrounding directory says, which breaks a second service reading the same tree. Give both services a shared group and set the setgid bit on the directory with sudo chmod g+s /srv/files, so new files inherit the directory's group.

Everything works on the port and breaks behind the proxy. An application served under a subpath builds links from a prefix it has to be told about. Cloud Commander has --prefix for this. Where no such option exists, give the application its own subdomain and proxy the root path.

Which self-hosted file manager should you run?

  • One VPS, one or two directories, share links with an expiry, and maybe SSO later: FileBrowser Quantum.
  • Files that live somewhere else, an S3 bucket or an SFTP host or a NAS over SMB, and you want one web view across them: Filestash, inside the free tier's limits.
  • Other people need accounts, quotas, and uploads over SFTP or FTPS: SFTPGo, treating the web client as a useful extra rather than the reason you chose it.
  • A personal tool with an editor and a terminal, reached through an SSH tunnel and never published: Cloud Commander.
  • Nextcloud already running and an existing directory to expose: the External Storage app, and no new software at all.

Whatever you pick, the deployment matters more than the choice. One directory, a non-root user, a port bound to localhost, and authentication in front. A file manager set up that way is a convenience. The same software pointed at / with a shared password is a remote shell with a nice interface.

FAQ

Is FileBrowser still safe to run in 2026?

The upstream filebrowser/filebrowser README states that File Browser is archived on 2026-09-01, with no further releases, bug fixes or security fixes. The code still runs, and unpatched software with write access to your filesystem is a risk that grows over time. If you keep it, follow the project's own advice: no direct exposure to the internet, a reverse proxy doing TLS and its own authentication, the command runner left disabled, and an unprivileged container with only the served directory mounted. Note also that its sessions are self-contained JWTs rather than server-side identifiers, so they cannot be revoked, and a changed password does not invalidate an issued token. For a new install, use the FileBrowser Quantum fork, published as the gtstef/filebrowser image, where development continues.

Can a self-hosted file manager use my existing SSO?

FileBrowser Quantum supports OIDC, LDAP and a proxy header mode, so it can sit behind an existing identity provider without a second user list. SFTPGo's OpenID Connect integration is in the open source build and maps identity provider users to SFTPGo admins and users for both the WebAdmin and WebClient interfaces. Filestash is the exception to watch: its pricing page places SSO (SAML, OIDC and LDAP) in the paid self-hosted tier from $50 per month as of August 2026, while the free tier is listed as AGPL v3 with up to 3 users. When an application has no SSO support at all, the fallback is forward authentication at the reverse proxy, which protects the login page but leaves the application's internal permissions untouched.

SFTPGo has the most complete implementation. A user creates an HTTP/S link from the WebClient and can limit the number of downloads and uploads, set a password, restrict access by source IP address, and set an automatic expiration date. FileBrowser Quantum supports shares with an expiration time, with access either anonymous or restricted to a user, plus per share permissions for viewing, editing and uploading. Filestash has a share feature as well, and it is the one case where the server keeps a persistent encrypted copy of the storage credentials, since the link has to work when your browser session is gone. Cloud Commander has no share links at all.

Is it safe to point a file manager at / if I am the only user?

No, and the risk is not really about trusting yourself. The process holds read and write access to everything its user account can reach, so any path into that session, a stolen cookie, an unpatched bug in the upload handler, a reused password, becomes access to /etc, to your SSH keys and to every service's data directory. Scope the mount to one directory instead: /srv/files rather than /. Cloud Commander is where this bites hardest, because its published Docker command mounts the host root at /mnt/fs and its sample config sets "root": "/" with "auth": false. Change both before that container listens on anything except localhost.

#file-manager#filebrowser#sftpgo#self-hosting#storage