configuration - mescon/Muximux GitHub Wiki
Muximux is configured via a single YAML file. By default, this is data/config.yaml (inside the data directory). The data directory defaults to data/ relative to the binary's location and can be changed with --data or MUXIMUX_DATA. You can also override the config path directly:
# Change the data directory (config will be at /opt/muximux/data/config.yaml)
muximux --data /opt/muximux/data
# Or override the config path explicitly
muximux --config /path/to/config.yaml# ─── Server ─────────────────────────────────────
server:
listen: ":8080" # Listen address (default: ":8080")
title: "Muximux" # Page title shown in browser tab
language: en # Interface language (BCP 47 tag, e.g. "en", "sv")
log_level: info # Log verbosity: debug, info, warn, error
log_format: text # Log output format: text or json
proxy_timeout: 30s # Global timeout for proxied requests
base_path: "" # Subpath for reverse proxy (e.g. "/muximux")
# Optional: TLS / HTTPS
tls:
domain: "" # Auto-HTTPS domain (requires email)
email: "" # Let's Encrypt registration email
cert: "" # Path to TLS certificate PEM
key: "" # Path to TLS private key PEM
# Replaced by `gateway_sites:` below. If you're upgrading from
# 3.0.x and still have this set, Muximux converts it to
# `gateway_sites:` on first boot and saves backups of both the
# original `config.yaml` and your Caddyfile alongside them.
gateway: "" # legacy
# Optional: Override the bind address for the embedded Caddy that
# serves gateway sites. Useful when (a) you don't want to grant
# CAP_NET_BIND_SERVICE / run as root, or (b) an upstream proxy
# terminates TLS and you want Caddy on a high port behind it.
# When set, gateway sites are emitted with http://<domain>:<port>
# and auto-https is disabled. Empty = legacy behaviour (:80/:443).
gateway_listen: "" # e.g. ":8443"
# Optional: cookie scope for cross-subdomain session sharing.
# Required when any entry in gateway_sites has require_auth=true,
# otherwise the session cookie won't reach the gated subdomain.
# Leading dot is optional (browsers normalise).
session_cookie_domain: "" # e.g. ".example.com"
# Optional: declarative gateway sites served by the embedded Caddy.
# See the Gateway Examples and Gateway Auth Gate wiki pages for the
# full reference. Each entry can optionally gate access behind the
# Muximux session via require_auth + min_role + allowed_groups.
gateway_sites:
- domain: sonarr.example.com # required
backend_url: http://192.168.1.5:8989 # required
tls: auto # auto | none | custom
tls_cert: "" # required when tls=custom
tls_key: "" # required when tls=custom
streaming: false # disable Caddy response buffering for live data
strip_frame_blockers: true # strip X-Frame-Options for iframe embedding
forwarded_headers: true # forward X-Forwarded-Proto/Host/Real-IP
backend_skip_tls_verify: false # skip verifying a self-signed HTTPS backend (Proxmox); https backends only
proxy_headers: {} # extra headers injected on upstream requests
# Optional: also surface this site in the dashboard menu
app_name: "" # link to an existing app by name
# Gateway auth gate (3.1.0+) -- gate this subdomain behind Muximux login
require_auth: false # 302 anonymous visitors to /login first
min_role: "" # "user" / "power-user" / "admin"; empty = any signed-in user
allowed_groups: [] # case-insensitive; admins bypass
# Auto-populated by Docker discovery on import; do not hand-edit.
# Presence of docker_key locks backend_url to the refresh poller.
docker_key: ""
docker_endpoint: ""
docker_strategy: ""
# ─── Authentication ─────────────────────────────
auth:
method: none # none, builtin, forward_auth, oidc
setup_complete: false # Set automatically after onboarding wizard completes
session_max_age: 24h # Session duration (default: 24h)
secure_cookies: false # Require HTTPS for session cookies
api_key_hash: "" # Optional bcrypt hash of API key for X-Api-Key auth
# Builtin auth users
users:
- username: ""
password_hash: "" # bcrypt hash (see Authentication page)
role: admin # admin, power-user, or user
email: "" # Optional
display_name: "" # Optional
groups: [] # Optional group memberships (for allowed_groups filtering)
# Forward auth settings
trusted_proxies: [] # CIDR ranges, e.g., ["192.168.0.0/16"]
headers:
user: Remote-User
email: Remote-Email
groups: Remote-Groups
name: Remote-Name # Note: key is "name" not "display_name"
logout_url: "" # Auth provider logout URL (e.g., https://auth.example.com/logout)
forward_auth_admin_groups: [] # Groups that grant admin (default: admin, admins, administrators)
# OIDC settings
oidc:
enabled: false
issuer_url: ""
client_id: ""
client_secret: "" # Supports ${ENV_VAR} syntax
redirect_url: "" # Must match provider callback URL
scopes: [openid, profile, email]
username_claim: preferred_username
email_claim: email
groups_claim: groups
display_name_claim: name
admin_groups: [] # Groups that grant admin role
provider_logout: false # End the session at the provider on logout
post_logout_redirect_url: "" # Default: <origin of redirect_url>/login?logged_out=1; register it at the provider
logout_url: "" # End-session URL for providers without one in discovery
auto_redirect: false # Login page goes straight to SSO (except after logout, ?local=1 or an error)
disable_local_login: false # Refuse password sign-in while OIDC is on (API keys still work)
# ─── Theme ─────────────────────────────────────
theme:
family: default # Theme family: default, nord, dracula, etc.
variant: system # dark, light, system (follow OS preference)
# ─── Navigation ─────────────────────────────────
navigation:
position: top # top, left, right, bottom, floating
width: 220px # Sidebar width
auto_hide: false
auto_hide_delay: 0.5s
show_on_hover: true
show_labels: true
show_logo: true
show_home_button: true # Home button in the navigation (required for the splash screen)
show_app_colors: true
show_icon_background: false
show_splash_on_startup: false
show_shadow: true
dynamic_tab_branding: false # Browser tab shows the active app's name and icon
icon_scale: 1.0 # Icon zoom multiplier
bar_style: grouped # grouped or flat (top/bottom bars only)
floating_position: bottom-right # bottom-right, bottom-left, top-right, top-left
hide_sidebar_footer: false # Hide the footer section in sidebars
show_docker_badge: true # Docker whale on Docker-discovered apps in the nav and overview (admins only)
max_open_tabs: 0 # Max app tabs kept alive in memory (0 = unlimited)
# ─── Icons ──────────────────────────────────────
icons:
dashboard_icons:
enabled: true
mode: on_demand # on_demand, prefetch, offline
cache_dir: icons/dashboard # Where fetched dashboard icons are cached
cache_ttl: 7d # How long a cached icon is reused before refetch
# ─── Health Monitoring ──────────────────────────
health:
enabled: true
interval: 30s
timeout: 5s
# ─── Docker Discovery (optional, opt-in) ───────
# Connect to a Docker daemon to discover running
# containers and keep their URLs current. See the
# Docker Discovery wiki page for the full guide.
discovery:
docker:
enabled: false # opt-in; requires socket access
endpoint: unix:///var/run/docker.sock # or tcp://host:2376
network_strategy: container_ip # container_ip | container_dns | host_port | host_docker_internal
network_filter: "" # scope scans to one docker network when set
host_ip: "" # required by host_port strategy
refresh_interval: 60s # poller cadence
auto_import: off # off (default) | add | update | sync; auto-import muximux.*-labeled containers
require_explicit_enable: false # auto-import only containers labelled muximux.app.enabled=true
lifecycle_enabled: false # allow start/stop/restart of tracked containers (requires :rw socket mount)
lifecycle_min_role: admin # min role for lifecycle controls
lifecycle_allowed_groups: [] # additionally require group membership
health_badge_placement: overview # overview | overview_and_nav | off
tls: # only needed for tcp:// with mTLS
enabled: false
ca_cert: "" # CA bundle that signed the daemon cert
client_cert: "" # client certificate (PEM)
client_key: "" # client key (PEM, chmod 600)
# ─── Keyboard Shortcuts ────────────────────────
keybindings:
bindings: {} # Custom overrides only; defaults managed client-side
# ─── Groups ─────────────────────────────────────
groups:
- name: Media
icon:
type: lucide
name: play
color: "#e5a00d"
order: 1
expanded: true
# Server-owned; written by Docker discovery, ignored in API payloads:
# docker_managed: true # group created by discovery; muximux.group.* labels apply
# docker_order: true # the group's order comes from a muximux.group.order label
# ─── Apps ───────────────────────────────────────
apps:
- name: Plex
url: http://plex:32400
# health_url: http://plex:32400/web # Optional custom health endpoint
icon:
type: dashboard
name: plex
# variant: light # Icon variant (dashboard icons only)
# file: healarr # Filename in data/icons/ (type: custom only)
# url: https://example.com/icon.png # Remote image URL (type: url only)
# color: "#ff0000" # Tint color (Lucide only)
# background: "#1a1a2e" # Icon background override
# invert: false # Invert icon colors (dark ↔ light)
color: "#e5a00d"
group: Media
order: 1
enabled: true
default: true
pinned: false # Show as a button in the top/bottom bar, ahead of the group dropdowns (stays in its group)
open_mode: iframe # iframe, new_tab, new_window, redirect, http_action
proxy: false
proxy_skip_tls_verify: true # Skip TLS certificate verification (default: true)
proxy_headers: # Custom headers sent to the backend
X-Custom-Header: value
forwarded_headers: true # Send X-Forwarded-For/Host/Proto and X-Real-IP to the backend; false sends none (for backends that reject them)
scale: 1.0
health_check: true # Opt-in: set true to enable health monitoring for this app
shortcut: null # Assign a number key (1-9) for quick switching
min_role: "" # Minimum role to see this app (user, power-user, admin)
force_icon_background: false # Show icon background even when global setting is off
auth_bypass: []
access:
roles: []
users: []
# Docker tracking; written by discovery (Discover import, auto-import, Re-link).
# Server-owned: ignored in API payloads. Editing url by hand detaches the app.
# docker_key: "label:plex" # tracking key (label:, swarm:, compose:, name:, id:)
# docker_endpoint: unix:///var/run/docker.sock
# docker_strategy: container_ip
# docker_managed_url: http://172.18.0.5:32400 # URL the poller last wrote
# docker_managed_health_check: true # value of the muximux.app.health_check label
# docker_auto: true # created by auto-import (only these are updated or removed by it)muximux [flags]
--data PATH Base data directory (default: data/ beside binary, env: MUXIMUX_DATA)
--config PATH Override config file path (default: <data>/config.yaml, env: MUXIMUX_CONFIG)
--listen ADDR Override listen address (env: MUXIMUX_LISTEN)
--base-path PATH Subpath for reverse proxy (e.g. /muximux, env: MUXIMUX_BASE_PATH)
--version Show version and exit
Precedence: CLI flag > environment variable > config file value > default.
Use ${VARIABLE_NAME} in any string value to reference environment variables. This is useful for keeping secrets out of config files:
auth:
oidc:
client_secret: ${OIDC_CLIENT_SECRET}
api_key_hash: ${MUXIMUX_API_KEY_HASH}
apps:
- name: Plex
url: ${PLEX_URL}If the referenced environment variable is not set, the ${VAR} reference is left as-is (the literal string is preserved). This avoids silently deleting values when an env var is missing.
This is the complete list of environment variables Muximux reads. Most of them override a config.yaml setting without needing ${VAR} syntax in the file.
A variable that is set but empty or only whitespace counts as unset, for every variable below. A Compose entry such as MUXIMUX_LISTEN=${LISTEN} therefore leaves the config file value in place when LISTEN is not defined, instead of forcing an empty value.
| Variable | Overrides | Values | Default |
|---|---|---|---|
MUXIMUX_DATA |
Data directory (flag --data) |
Path |
data/ beside the binary |
MUXIMUX_CONFIG |
Config file path (flag --config) |
Path | <data>/config.yaml |
MUXIMUX_LISTEN |
server.listen (flag --listen) |
Address, e.g. :9090
|
From config file |
MUXIMUX_BASE_PATH |
server.base_path (flag --base-path) |
Path, e.g. /muximux
|
From config file (empty = root) |
MUXIMUX_LOG_LEVEL |
server.log_level |
debug, info, warn, error
|
From config file |
MUXIMUX_LOG_FORMAT |
server.log_format |
text or json
|
From config file |
The Docker image starts Muximux with --data /app/data, and a flag wins over its variable, so MUXIMUX_DATA has no effect in the container unless you replace the command.
| Variable | Overrides | Values | Default |
|---|---|---|---|
MUXIMUX_DISCOVERY_AUTO_IMPORT |
discovery.docker.auto_import |
off, add, update, sync
|
From config file |
MUXIMUX_DISCOVERY_REQUIRE_EXPLICIT_ENABLE |
discovery.docker.require_explicit_enable |
Boolean | From config file |
An unknown MUXIMUX_DISCOVERY_AUTO_IMPORT value is treated as off with a warning. An invalid MUXIMUX_DISCOVERY_REQUIRE_EXPLICIT_ENABLE value is ignored with a warning and the config file value stays. An empty value counts as unset for both. See Docker Discovery > Automatic Import.
| Variable | Overrides | Values |
|---|---|---|
MUXIMUX_AUTH_METHOD |
auth.method |
none, builtin, forward_auth, oidc
|
MUXIMUX_OIDC_ENABLED |
auth.oidc.enabled |
Boolean |
MUXIMUX_OIDC_ISSUER_URL |
auth.oidc.issuer_url |
URL |
MUXIMUX_OIDC_CLIENT_ID |
auth.oidc.client_id |
String |
MUXIMUX_OIDC_CLIENT_ID_FILE |
auth.oidc.client_id |
Path of a file holding the value (Docker secrets) |
MUXIMUX_OIDC_CLIENT_SECRET |
auth.oidc.client_secret |
String |
MUXIMUX_OIDC_CLIENT_SECRET_FILE |
auth.oidc.client_secret |
Path of a file holding the value (Docker secrets) |
MUXIMUX_OIDC_REDIRECT_URL |
auth.oidc.redirect_url |
Absolute http(s) URL |
MUXIMUX_OIDC_SCOPES |
auth.oidc.scopes |
List, separated by commas and/or spaces |
MUXIMUX_OIDC_USERNAME_CLAIM |
auth.oidc.username_claim |
String |
MUXIMUX_OIDC_EMAIL_CLAIM |
auth.oidc.email_claim |
String |
MUXIMUX_OIDC_GROUPS_CLAIM |
auth.oidc.groups_claim |
String |
MUXIMUX_OIDC_DISPLAY_NAME_CLAIM |
auth.oidc.display_name_claim |
String |
MUXIMUX_OIDC_ADMIN_GROUPS |
auth.oidc.admin_groups |
List, separated by commas |
MUXIMUX_OIDC_PROVIDER_LOGOUT |
auth.oidc.provider_logout |
Boolean |
MUXIMUX_OIDC_POST_LOGOUT_REDIRECT_URL |
auth.oidc.post_logout_redirect_url |
Absolute http(s) URL |
MUXIMUX_OIDC_LOGOUT_URL |
auth.oidc.logout_url |
Absolute http(s) URL |
MUXIMUX_OIDC_AUTO_REDIRECT |
auth.oidc.auto_redirect |
Boolean |
MUXIMUX_OIDC_DISABLE_LOCAL_LOGIN |
auth.oidc.disable_local_login |
Boolean |
Each defaults to the config file value. An empty or blank auth variable counts as unset, including an empty X_FILE, so X=value with X_FILE= uses the value. Setting both X and X_FILE is an error. Invalid auth configuration stops startup instead of leaving the instance open, for example an unknown MUXIMUX_AUTH_METHOD, or OIDC variables without MUXIMUX_AUTH_METHOD on an instance whose config.yaml is not already on OIDC. An unknown MUXIMUX_OIDC_* name is logged as a warning. The full rules, with examples, are in Configuring OIDC with environment variables; Compose and Swarm examples with Docker secrets are in Deployment.
These are read by the Docker image's entrypoint, not by Muximux itself:
| Variable | Purpose | Default |
|---|---|---|
PUID, PGID
|
User and group ID the process runs as, to match your bind-mounted data/ directory |
1000 |
DOCKER_SOCKET |
Path of the Docker socket whose group the entrypoint detects | /var/run/docker.sock |
DOCKER_GID |
Force the group ID used for Docker socket access instead of detecting it | Detected |
TZ |
Time zone | UTC |
-
Booleans accept
true,1,yes,onandfalse,0,no,off(case-insensitive). -
Precedence: command-line flag, then environment variable, then the
config.yamlvalue (including a${VAR}reference there), then the built-in default. -
Memory only. An override changes the running configuration only. Saving from Settings (or through the API) writes the value
config.yamlalready had, never the override, so removing the variable brings the file's value back on the next start. The one exception is a setup wizard completed underMUXIMUX_AUTH_METHOD=builtin, which savesmethod: builtinwith the new admin. -
Locked in Settings. A field whose value comes from a variable or flag is shown locked, labelled with the variable's name ("From MUXIMUX_LISTEN", "Set by MUXIMUX_OIDC_CLIENT_SECRET_FILE"). The admin
GET /api/configresponse lists these fields inenv_overrides(field to variable name, never the value); see the API Reference. -
First start. The overrides also apply when there is no
config.yamlyet, so a container configured only through the environment starts with those values. - Restart. Variables are read at startup. Change one and restart Muximux.
Muximux validates the configuration on startup and will refuse to start if the configuration is invalid. The following rules are enforced:
- If
tls.domainis set,tls.emailis required (for Let's Encrypt registration). -
tls.certandtls.keymust both be set or both empty. You cannot provide only one. -
tls.domainandtls.certare mutually exclusive. Use either auto-HTTPS or manual certificates, not both. - Legacy
gateway:(Caddyfile path) configs from 3.0.x are auto-migrated togateway_sites:on first boot under 3.1.0 (clean conversions only; lossy ones surface a hard error pointing atmuximux migrate-gateway). - If any
gateway_sites[]entry hasrequire_auth: true,server.session_cookie_domainis required and every gated site must be a subdomain of it (real host-suffix check:evilexample.comis not a subdomain ofexample.com). -
gateway_sites[].min_rolemust be one ofuser,power-user,adminwhen set (empty means "any authenticated user"). -
server.gateway_listenmust be a valid bind address (e.g.,:8443or127.0.0.1:8443).
Most settings can be changed through the Settings panel while Muximux is running. Changes are saved to data/config.yaml immediately and take effect without restarting.
The following settings require a restart to take effect:
-
server.listen(listen address/port) -
server.base_path(subpath prefix; must be a plain path such as/muximux: no leading//, backslashes, dot segments, whitespace,?or#) -
server.tls.*(all TLS settings) -
server.gateway_listen(overrides the bind port for the embedded Caddy) -
server.session_cookie_domain(cookie scope; takes effect on next login) -
server.gateway_sites:schema changes (the auth gate; backend swaps and TLS-mode flips reload Caddy in place)
When Muximux saves the configuration it writes defaults for fields you omitted, so a saved config.yaml can contain more keys than you wrote by hand. Settings saves are a three-way merge: changes made on the server while Settings is open (a Docker URL refresh, auto-import, another tab) are kept, and fields you did not touch keep the server's value. A save that would create two apps or groups with the same name is refused (409) and Settings blocks Save until one is renamed. Settings closes only after the save succeeds; on failure it stays open with the server's message. Closing it with unsaved changes asks first, and Discard also reverts keybinding and theme previews. Every open browser gets a live update (the config_updated event) and refetches the config; the page reloads only when the language changed, and an open Settings dialog keeps its unsaved edits on top of the new state.
A restored backup is read exactly like config.yaml at startup: ${VAR} expansion, defaults and validation. Restore reloads users, auth, OIDC, discovery and proxy routes at once; listen address, TLS, base-path routing and session cookie settings still need a restart.
Everything else -- navigation, themes, apps, groups, icons, keybindings, health monitoring, log level, log format -- is applied immediately.
You can export your configuration as a downloadable YAML file and import it on another instance:
- Export strips sensitive data (password hashes, OIDC secrets, API key hashes) before download.
- Import parses the uploaded YAML, validates it, and shows a preview so you can review before applying.
This is available in the Settings panel under the General tab, or via the API (see API Reference).
The configuration can also be read and updated via the API:
-
GET /api/config-- Retrieve the current configuration (any authenticated user). -
PUT /api/config-- Update the full configuration (admin role required). -
GET /api/appearance-- Read-only snapshot of language, theme id, and theme colors, for embedded apps that want to sync their own appearance (see Apps > Appearance API).
See the API Reference for details.