A two-person studio and a 50-person office both want the same thing from a self-hosted LLM: one machine holds the GPU, several people need to reach it, and none of them should be able to reach it by accident. The household guide answers that for the family Wi-Fi case. A team has the same shape but different answers everywhere: authentication instead of overlay-network trust, a reverse proxy with HTTPS instead of a tailnet, role-based access instead of one shared admin password, parallel slots instead of “wait your turn”.
What changes between a household and a team
A household trusts every device on the LAN. A team cannot: the office Wi-Fi is also the guest network, contractors come and go, and laptops sync from coffee shops. The household page leans on overlay networks (Tailscale or WireGuard) to make the LAN-side address unreachable from outside it; the same trick works for a team only if every team member is willing to install a client, which most are not. The team page that follows instead puts the model behind a TLS-terminating reverse proxy on the open internet, then puts authentication in front of that.
That change has a real cost. A reverse proxy means DNS, certificates, rate limits, and access logs that a household never sees. It also unlocks things a household page cannot offer: a stable hostname the team pastes into Slack, single sign-on through the identity provider the company already runs, audit logs per user, and the ability to take a laptop on the road without configuring a client.
Pick a reverse proxy that issues its own certificates
HTTPS is non-negotiable on the open internet. Cookies, headers, and prompt contents all need encryption, and modern browsers warn against every form on a plain HTTP origin. The proxy is what terminates TLS.
Caddy’s automatic HTTPS is the lowest-friction option for a small team that has control over DNS. Activation is implicit - put a hostname in the Caddyfile and Caddy “obtains and renews certificates for qualifying domain names”, “redirects HTTP to HTTPS”, and serves local names via its own self-signed CA. The supported challenges are HTTP-01 (default), TLS-ALPN-01 (default), and DNS-01 (needed for wildcard). Caddy’s internal limit is “10 attempts per ACME account per 10 seconds”, so a misconfigured host spams Let’s Encrypt at most that often before backing off for up to a day. If Let’s Encrypt cannot issue a cert Caddy falls back to ZeroSSL automatically, since both are enabled by default.
Traefik is the other mainstream option. Its router/entrypoint/middleware model is more verbose than a Caddyfile but composes well with Docker labels and Kubernetes ingress. The TLS section of the Traefik docs covers the same ACME challenge set as Caddy, with HTTP-01 and TLS-ALPN-01 supported out of the box and DNS-01 available through one of the provider plugins.
Behind either proxy, Open WebUI’s hardening guide flags two things the proxy must do. First, set WEBUI_SESSION_COOKIE_SECURE=true and HSTS=max-age=31536000;includeSubDomains so browsers never send the session cookie back over plaintext. Second, restrict FORWARDED_ALLOW_IPS (which the guide shows as 192.168.1.100 in its example) to the proxy’s IP, because Open WebUI’s --forwarded-allow-ips defaults to * and trusting every source for X-Forwarded-For is a different threat when the proxy is on the public internet than when it is on a home LAN.
Put a real identity provider in front of it
A reverse proxy with no auth is no safer than Ollama bound to 0.0.0.0. Three layers fit a small team.
The lightest is oauth2-proxy: “a flexible, open-source tool that can act as either a standalone reverse proxy or a middleware component integrated into existing reverse proxy or load balancer setups”, MIT-licensed, ~14.9k stars at the time of writing. Configure it with provider= and the appropriate client credentials, point it at your upstream, and forward X-Forwarded-Email and X-Forwarded-Name to the chat UI. The Open WebUI hardening guide explains the corresponding Open WebUI variables (WEBUI_AUTH_TRUSTED_EMAIL_HEADER=X-Forwarded-Email, WEBUI_AUTH_TRUSTED_NAME_HEADER=X-Forwarded-Name) and warns that the proxy must “strip these headers from incoming client requests before injecting its own values. If the proxy does not strip them, any client can send a forged header and authenticate as any user.”
The middle option is a small OpenID Connect (OIDC) server in front of oauth2-proxy. Pocket-ID is built for exactly this scale: BSD-2-Clause, ~9.1k stars, and “the most user-friendly OpenID Connect Certified and OAuth 2.0 provider that lets users sign in to your applications with passkeys.” Passkey-only auth means no password resets, which is the single biggest operational win for a five-person team that does not have a help desk.
The heavyweight option is Authentik - a full SSO suite, useful when the team already runs multiple apps. The Docker Compose install ships “server, worker, postgres, redis” services and “by default, authentik listens internally on port 9000 for HTTP and 9443 for HTTPS”; mapping those to 80/443 is a COMPOSE_PORT_HTTP / COMPOSE_PORT_HTTPS env change. The required environment variables are a database password (PG_PASS, base64, “passwords longer than 99 characters are not supported”) and a signing key (AUTHENTIK_SECRET_KEY). Authentik is overkill for one chat app; it earns its keep when there are three or more apps to share.
Lock the chat UI to known users, then to known roles
Open WebUI is what most teams use for the chat front end, and three of its env vars do nearly all the lockdown work. The env reference states ENABLE_SIGNUP defaults to True (“Toggles user account creation”), DEFAULT_USER_ROLE defaults to pending (“Sets the default role assigned to new users”), and ENABLE_LOGIN_FORM defaults to True. The safe pattern is to flip ENABLE_SIGNUP=False after the first admin is created (or wire it to False from the start and create accounts through the admin panel), keep the login form on for staff, and promote each account from pending to a real role by hand. ENABLE_SIGNUP_PASSWORD_CONFIRMATION=False is the documented default, so a second-pass typo does not by itself block account creation.
When the team uses OAuth through the reverse proxy, set OAUTH_ALLOWED_DOMAINS=yourcompany.com per the same hardening guide so a personal Google account on the same oauth2-proxy cannot ride in. The guide’s LDAP variables (ENABLE_LDAP=true, LDAP_USE_TLS=true, LDAP_VALIDATE_CERT=true) cover the Active Directory case for teams that have not adopted an OIDC IdP.
How many users the model actually serves
Concurrency is what makes a “small team” page different from a hobbyist page. The Ollama FAQ documents the three dials. OLLAMA_NUM_PARALLEL is “the maximum number of parallel requests each model will process at the same time, default 1”. OLLAMA_MAX_LOADED_MODELS is “the maximum number of models that can be loaded concurrently provided they fit in available memory. The default is 3 times the number of GPUs or 3 for CPU inference.” OLLAMA_KEEP_ALIVE is “By default models are kept in memory for 5 minutes before being unloaded.” For a team that means going from the defaults (1 request, 3 models, 5 minutes idle) to something like OLLAMA_NUM_PARALLEL=4 (the rough number of GPUs in a single workstation), OLLAMA_KEEP_ALIVE=30m so a returning user does not pay the model-load penalty, and OLLAMA_MAX_LOADED_MODELS left at default unless the team routinely runs a small model alongside a large one.
| Setting | Default | Team-sized value | Why |
|---|---|---|---|
OLLAMA_NUM_PARALLEL | 1 | matches GPU count (commonly 2-4) | more than one user can prompt without queueing |
OLLAMA_KEEP_ALIVE | 5m | 30m to 1h | a returning user should not reload weights |
OLLAMA_MAX_LOADED_MODELS | 3 (or 3 x GPUs) | usually left at default | controls RAM, raise only if the team routinely runs multiple models |
For llama.cpp, the server’s README documents --parallel as “number of server slots (default: -1, -1 = auto)” and --cont-batching as “whether to enable continuous batching (a.k.a dynamic batching) (default: enabled).” Continuous batching is what lets a single slot serve several users’ requests in flight at once. Add --api-key (default none) when the llama.cpp server sits directly on the network; if the proxy already authenticates, leaving the API key off and depending on the proxy is normal.
Audit, rate limits, and the cert renewal you forget about
The hardening guide is explicit about scope: “rate limiting, brute-force prevention, and DDoS protection should be handled at the proxy or network layer.” The proxy is where you throttle login attempts, drop requests to scanner paths like /wp-admin.php, and tail logs per source IP. Caddy’s reverse_proxy directive supports active health checks (health_uri, health_interval, health_status) and load balancing (lb_policy with options like round_robin, least_conn, ip_hash); if you scale from one machine to two, the proxy becomes the load balancer.
The one operational gotcha a household page never warns about is certificate renewal. Caddy and Traefik renew automatically in the background, but the renewal spams Let’s Encrypt if something upstream (a firewall, a stale DNS record) is broken. Let’s Encrypt’s rate limits cap a brand-new domain at “50 certificates per Registered Domain per 7 days” and “5 certificates per Exact Set of Identifiers per 7 days”; the “Authorization Failures per Identifier per Account” cap is “5 failures per hour” before the system throttles. Test against the staging environment - directory https://acme-staging-v02.api.letsencrypt.org/directory, much higher limits, certificates that browsers do not trust - until the renewal path is reliable, then switch to production.
Bottom line
For a household, the right shape is “LAN-bound model plus Tailscale”. For a team, the right shape is “model bound to 127.0.0.1, a reverse proxy (Caddy or Traefik) with automatic HTTPS in front of it, oauth2-proxy plus either Pocket-ID or Authentik for single sign-on, Open WebUI with ENABLE_SIGNUP=False and DEFAULT_USER_ROLE=pending set explicitly, Ollama parallel slots raised to match the GPUs you have, and rate limits enforced at the proxy.” The household page is the right starting point for a one-machine flat. This page is the right starting point for anything larger that wants SSO, a stable hostname, and audit logs.