Skip to content

File server

The file server is the part of Fancy Mumble that handles:

  • Custom server emote images.
  • Chat file attachments (the share dialog).

It is an HTTP service that listens on port 64739 by default. The listener speaks plain HTTP - TLS is not built in. On a private network that is fine; on the public internet, put it behind a TLS-terminating reverse proxy (see Behind a reverse proxy below) so that clients reach it as HTTPS.

Without it, file sharing and custom emotes are unavailable, the buttons are hidden in the app.

Mumble Clientdesktop / mobileBrowserReverse Proxynginx · Traefik · Caddy(optional)File Serverport 64739 plain HTTPNo built-in TLS.Skip the proxy only ona trusted private network.HTTPS 443HTTPS 443HTTP 64739 (behind proxy)HTTP 64739 (direct, LAN only)HTTP 64739 (direct, LAN only)

The Docker image ships with the file server already enabled. The defaults are:

plugin.file-server.enabled=true
plugin.file-server.storagePath=/data/file-server-storage
plugin.file-server.bindAddress=0.0.0.0
plugin.file-server.port=64739
plugin.file-server.tlsTerminatedByProxy=true
; plugin.file-server.baseUrl=https://your-domain.example/files
; plugin.file-server.allowedOrigins=https://your-domain.example

On a fresh container started with the official image, the config file is generated at boot under:

/data/mumble_server_config.ini

This is the file the entrypoint script writes whenever you change a MUMBLE_CONFIG_* environment variable. It is regenerated on every container start, so editing it directly does not survive a restart unless you also set MUMBLE_CUSTOM_CONFIG_FILE to point at a file you own.

To set plugin.* keys for the first time:

  1. Copy the sample config out of the image to your host:

    Terminal window
    docker cp mumble-server:/data/mumble_server_config.ini ./mumble-server.ini

    Or grab the template from the Docker repo at mumble-server.ini.example.

  2. Edit the plugin.file-server.* keys to your taste.

  3. Mount the file back into the container and tell the entrypoint to use it instead of the env-var-generated one:

    environment:
    MUMBLE_CUSTOM_CONFIG_FILE: /data/mumble-server.ini
    volumes:
    - ./mumble-server.ini:/data/mumble-server.ini:ro
    - mumble-data:/data
  4. Restart the container:

    Terminal window
    docker compose up -d --force-recreate mumble-server
KeyDefaultDescription
plugin.file-server.enabledtrueMaster toggle.
plugin.file-server.storagePath/data/file-server-storageWhere files live on disk. Must be absolute.
plugin.file-server.bindAddress127.0.0.1 (with safer default in the image)Address to listen on. Use 0.0.0.0 to expose directly.
plugin.file-server.port64739TCP port.
plugin.file-server.tlsTerminatedByProxyfalseSet to true when a reverse proxy handles TLS.
plugin.file-server.baseUrlunsetPublic URL clients use. Falls back to http://host:port when empty.
plugin.file-server.allowedOriginsunsetComma-separated CORS origins. Empty blocks browser usage.
plugin.file-server.maxUploadSizeBytes52428800 (50 MB)Per-file upload limit.
plugin.file-server.retentionDays90How long public files live.

In docker-compose.yml:

ports:
- "64739:64739/tcp"

Without this port exposed externally, the file server is only reachable from inside the container. Useful if you put a reverse proxy in front and only expose the proxy.

Recommended for any deployment reachable from the public internet. The proxy accepts HTTPS from clients and forwards it as plain HTTP to the file server. Tell the file server to trust the proxy headers:

plugin.file-server.tlsTerminatedByProxy=true
plugin.file-server.baseUrl=https://files.example.com
plugin.file-server.allowedOrigins=https://files.example.com

A minimal nginx config:

server {
listen 443 ssl http2;
server_name files.example.com;
ssl_certificate /etc/letsencrypt/live/files.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/files.example.com/privkey.pem;
client_max_body_size 100m;
location / {
proxy_pass http://mumble-server:64739;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
  • Directory/data/file-server-storage/
    • Directoryemotes/ custom server emotes, keyed by shortcode
    • Directoryattachments/ chat attachments
      • Directorypublic/ public-mode uploads
      • Directorypassword/ password-protected uploads
      • Directorysession/ session-only uploads (cleaned up)
    • index.sqlite metadata, not file data

Back up the whole file-server-storage folder along with the mumble-server.sqlite database for a complete backup.

When a user uploads a file, they pick one of three modes. See Files and images for the user view.

ModeWho can downloadLifetime
PublicAnyone with the linkretentionDays
PasswordAnyone with the link AND the passwordretentionDays
SessionOnly currently connected usersA short while after every recipient disconnects

You can restrict who is allowed to use public/password modes per role. See Permission flags.

For a 50-user server, plan for roughly:

  • Avatars: 100 KB per user. Negligible.
  • Custom emotes: 50 KB per emote, often a few hundred per server.
  • Attachments: this is the variable. A busy media-sharing server can use 10 GB or more per month. Cap with maxUploadSizeBytes and retentionDays.

Add monitoring on /data/file-server-storage to catch surprises.

plugin.file-server.enabled=false

The client UI hides the file-attach controls when no fancy-file-server-config message is received. Existing avatars and emotes still work as long as the storage directory and database are intact.

  • Paperclip is missing in chat: the file server is not enabled, or the client cannot reach the port. Check curl -I http://server:64739/healthz from a client machine.
  • CORS errors in browser builds: set allowedOrigins to your web client’s URL.
  • Uploads fail with 413: maxUploadSizeBytes is too small, or your reverse proxy’s client_max_body_size is.
  • Files stay forever: retentionDays=0 is “forever”. Set to a positive number for cleanup.

Continue with Link previews.