github mescon/Muximux v3.1.0
v3.1.0 - Docker Discovery & Gateway sites

3 hours ago

The big one. Two new features that probably matter more than any single 3.0.x point release: Muximux can now discover Docker containers and import them as apps with one click, and it can act as a single-sign-on gate in front of gateway-hosted subdomains. Plus a healthy round of catalog updates, hardening, and dependency bumps.

Stop hand-typing your homelab into config.yaml

If you run your apps in Docker (and let's be honest, you do), v3.1.0 will read your daemon and offer to do the boring part for you.

Open Settings -> Discovery, hit "Discover apps", and Muximux enumerates every running container, matches them against a curated catalog of ~30 self-hosted apps (Sonarr, Radarr, Plex, Jellyfin, qBittorrent, SABnzbd, Grafana, Portainer, Uptime Kuma, Seerr, the works), and shows you a list with sensible defaults: name, icon, port, URL. Tick the ones you want, hit Import, done.

Containers that aren't in the catalog still show up. They land with a "low confidence" chip, a name auto-derived from the container's name, and a per-row icon picker so you can finish the job without leaving the modal. No more "what did I name that thing again" while flipping between docker ps and the settings panel.

Each row lets you pick how the app gets exposed:

  • Direct -- the menu link goes straight to the container's URL. Works when the dashboard machine can reach the container's IP and the app doesn't set X-Frame-Options.
  • Proxy -- Muximux's built-in reverse proxy serves the app at /proxy/<slug>, rewriting paths and stripping iframe-busting headers. The "it just works" option for stubborn apps.
  • Gateway -- registered as a Caddy gateway site on a subdomain (e.g. sonarr.example.com), with automatic Let's Encrypt. Useful when you also want the app reachable from outside Muximux.

A background refresh poller keeps imported URLs current. If a container's IP shifts (compose restart, network rotation, ESXi migration), Muximux re-resolves it and updates config.yaml. If the new shape would break Caddy, Muximux rolls back to the working one and shows a divergence banner in the UI with the actual error so you can see what happened. No more "where did my Sonarr go?"

# Minimal opt-in (Docker socket mounted into the muximux container):
discovery:
  docker:
    enabled: true
    endpoint: unix:///var/run/docker.sock   # or tcp://host:2376 for remote daemon
    network_strategy: container_ip          # or "container_dns" / "host_port" / "host_docker_internal"
    refresh_interval: 60s

Apps that ship a muximux.app.* label on the container (muximux.app.name, muximux.app.icon, muximux.app.port, muximux.app.scheme, muximux.app.group, muximux.app.path, muximux.app.health, muximux.app.gateway.domain, muximux.discovery.id) get a green "high confidence" badge -- the operator gets the final say but the row needs no editing. Set muximux.discovery.id=<stable-key> on swarm tasks and --force-recreate flows to keep tracking stable across restarts.

Discovered entries show up with a "managed by discovery" badge on the App / Gateway-site edit forms. The source-of-truth fields (URL, container ID, image) are read-only by default -- click Detach to take ownership manually. The next scan offers a one-click re-link if you change your mind.

Resolves #316 (Docker auto-discovery).

Single sign-on across all your gateway subdomains

Until now, gateway sites in Muximux were a pure reverse proxy: open sonarr.example.com and you hit Sonarr directly. v3.1.0 lets you optionally put Muximux's login in front of any gateway site, turning Muximux into a forward-auth gate.

Useful when:

  • You're exposing apps that lack their own auth (Plex's web admin, Grafana with anonymous access, a custom Flask thing).
  • You want one login to authorise access to N gated subdomains -- Muximux's session cookie does the SSO.
  • You want defence in depth in front of an app that has auth but you'd rather not test that surface from the public internet.

Enable per site with two new fields:

server:
  session_cookie_domain: ".example.com"   # required for cookie scope across subdomains
  tls:
    domain: "muximux.example.com"
  gateway_sites:
    - domain: sonarr.example.com
      backend_url: http://10.0.0.5:8989
      tls: auto
      require_auth: true                  # gate this site
      min_role: user                      # optional; "user" / "power-user" / "admin"
      allowed_groups: [family, admins]    # optional; case-insensitive; admins bypass

The flow: a request hits sonarr.example.com, Caddy asks Muximux "is this visitor allowed?", and Muximux checks the session cookie plus the per-site permission rules. Allowed visitors get forwarded to the backend with X-Muximux-User and X-Muximux-Role headers attached, in case the backend wants to honour them. Anonymous visitors are redirected to the Muximux login page and bounced back to the original URL after signing in. Signed-in visitors who don't meet the role / group bar get a small "you're signed in but lack permission" page that names which check they failed. Every decision is audit-logged so you can review who reached what.

Logout (/logout on Muximux) invalidates the cookie across all gated subdomains at once -- one click, fully signed out everywhere.

Topology requirement: every gated site needs to be a subdomain of session_cookie_domain, otherwise the browser won't send the cookie across to it. Muximux's config validator enforces this at startup and refuses to start with a specific error pointing at the offending site, so misconfigurations fail loudly instead of silently looping visitors between the gate and the login page.

Full topology, troubleshooting matrix, and audit log format on the new Gateway Auth Gate wiki page.

You don't have to hand-edit config.yaml to bootstrap any of this either. The Gateway tab notices when you tick Require Muximux login without a cookie scope configured and surfaces an inline editor right in the warning -- type the parent domain (placeholder is auto-derived from the site you're editing), click Set cookie scope, and Muximux saves it for you. The warning then becomes a green "restart to take effect" nudge.

One more thing: running on :80 / :443 without root

If you've wanted to put Muximux directly on the public internet without running it as root or stacking a second reverse proxy in front, you now have two clean paths:

  • Grant the binary the Linux capability to bind privileged ports (setcap CAP_NET_BIND_SERVICE+eip /path/to/muximux). Recommended for production - least privilege.
  • Set server.gateway_listen: ":8443" (or any other unprivileged port). All gateway sites now serve over plain HTTP on that port, and you put your existing reverse proxy (Cloudflare Tunnel, an upstream Traefik, an external Caddy) in front to terminate TLS. Sites with tls: custom still serve their own HTTPS on the high port if you prefer to keep TLS at Muximux's Caddy.

If you try to bind 80/443 without either being in place, Muximux exits at startup with a clear error naming both fixes - no more digging through stack traces to figure out what went wrong.

Catalog updates

  • Readarr -- now annotated as EOL. The upstream project was archived; the catalog entry points users at active alternatives (Calibre-Web, Kavita) so new imports don't land on a dead app. Existing apps keep working; the annotation is informational. Resolves part of #333.
  • Seerr -- sct/overseerr-telegram-bot, sct/overseerr, linuxserver/overseerr, and fallenbagel/jellyseerr are now merged under a single Seerr catalog entry, reflecting the upstream project merger. One icon, one name, one set of defaults regardless of which image you're running. Resolves the rest of #333.

Breaking changes

  • server.gateway: (the Caddyfile path) is no longer supported. On first boot under 3.1.0 Muximux converts your existing Caddyfile to the new server.gateway_sites: YAML automatically and saves backups of both files with a .pre-3.1.0.bak suffix. Subsequent boots are clean. If your Caddyfile uses something the structured form can't represent (custom matchers, third-party plugins), Muximux won't silently rewrite it - run muximux migrate-gateway /path/to/sites.Caddyfile > sites.yaml to see what's blocking the auto path and finish the migration by hand. You can also rebuild your sites visually in Settings → Gateway.

Migration notes

Drop-in upgrade from 3.0.x. The legacy server.gateway: Caddyfile is auto-converted on first boot (see Breaking changes); both new big features (Docker discovery, auth gate) are strictly opt-in.

  • Docker discovery stays dormant unless you set discovery.docker.enabled: true and reachable endpoint: in config.yaml. Existing apps are untouched and never get a "managed by discovery" badge until you import them through the modal.
  • Gateway auth gate only activates on sites that set require_auth: true. Existing gateway_sites: entries keep their pure reverse-proxy behaviour. The new top-level server.session_cookie_domain: field is optional; setting it has no effect on non-gated sites or on the dashboard itself.
  • server.gateway_listen: is optional. Leave it unset to keep the previous behaviour (gateway sites bind to :443 / :80 and need either root or CAP_NET_BIND_SERVICE).
  • Readarr / Seerr catalog renames only affect new discovery imports. Existing apps named "Overseerr", "Jellyseerr", or "Readarr" keep their names, icons, and URLs; the catalog rename is metadata-only.
  • Config validation is now run on every PUT /api/config (Settings save), not just at startup. A bad session_cookie_domain / gated-site pairing is now rejected with HTTP 400 instead of being persisted and failing the next boot. This is strictly more defensive; configurations that loaded successfully before will continue to save successfully.

Added

  • Gateway sites are now declarative YAML. Define them under server.gateway_sites: in config.yaml with per-site TLS mode, proxy headers, streaming flag, iframe-blocker stripping, and (new in 3.1.0) the auth-gate fields. Edit them from Settings -> Gateway without dropping into a Caddyfile. The legacy server.gateway: Caddyfile path is gone in this release; clean Caddyfiles are auto-migrated on boot (with .pre-3.1.0.bak backups), and muximux migrate-gateway <caddyfile> is available for the lossy cases the auto path refuses to silently rewrite.
  • Onboarding wizard now configures Docker discovery. The apps step gained a "Have Docker?" opt-in card with endpoint and network-strategy fields. Ticking the box and finishing onboarding writes the discovery config so Settings -> Discovery lands ready to scan; the actual import still happens there.
  • govulncheck and golangci-lint now run as part of the local pre-push hook. Skipped cleanly if you don't have them installed; block the push if either reports something when you do.

Changed

  • All dependencies refreshed. Caddy 2.11.3, latest golang.org/x/crypto and x/term, the npm group (Vite, vitest, tailwindcss, svelte-sonner, eslint, kysely and others), and the GitHub CodeQL action all bumped to current latest. Seven Dependabot PRs absorbed locally before the tag so the post-release inbox stays quiet.

Security

  • Caddy 2.11.3 ships upstream security patches. A fastcgi-execution bypass (PHP / FrankenPHP issue), a more thorough fix for the vars module advisory, and two admin-socket auth-bypass fixes all land in this release because we follow Caddy's stable line.
  • Forward-auth endpoint is now loopback-only. /api/auth/forward (the receive side of Caddy's forward_auth directive) checks the caller's RemoteAddr and rejects anything that isn't a loopback address. This prevents a public visitor from probing session validity or gateway-site configuration by hitting the endpoint directly. Caddy's in-process forward_auth call always arrives over 127.0.0.1 so the change is transparent for normal use.
  • Hardened login-redirect URL construction. The next= parameter on the auth-gate's login redirect is now rebuilt from the config-validated gateway_sites[].domain rather than the raw X-Forwarded-Host header. Only the port suffix (legitimate when gateway_listen is set) is inherited from the request. Defence in depth against any future regression that might widen the upstream host-validation check.
  • Config-API validation is now strict-equivalent to startup: a malformed session_cookie_domain or gated-site pairing PUT through the Settings API is rejected with HTTP 400 and rolled back in memory before it can be persisted.

Don't miss a new Muximux release

NewReleases is sending notifications on new releases.