Configuration
AI Admin Panel is configured through environment variables, Docker Compose files, and service-specific configuration. This page covers all configurable aspects of the system.
Environment Variables
The primary configuration file is /opt/aiadminpanel/.env. All variables are read at startup.
Core Settings
| Variable | Default | Description |
|---|---|---|
PANEL_DOMAIN | (required — auto-filled by the installer's temporary domain when you don't have one) | Base domain for the panel |
MANAGED_DOMAIN | (unset) | true on installs using a claimed temporary domain (written by the installer) |
MANAGED_DNS_URL | https://dns.aiadminpanel.com | Claim service the temporary domain came from; the panel renews the lease against it |
MANAGED_DOMAIN_TOKEN_FILE | /run/secrets/managed_domain_token | Path to the claim credential the panel reads to verify email and renew the lease |
PANEL_PORT | 8080 | Internal HTTP port (Traefik proxies to this) |
DATABASE_URL | (auto-generated) | PostgreSQL connection string |
VALKEY_URL | valkey://valkey:6379 | Valkey connection string |
LOG_LEVEL | info | Log level: debug, info, warn, error |
ENVIRONMENT | production | Runtime environment: production, development |
MANAGED_DOMAIN_TOKEN_FILE points at a Docker Compose secret (managed_domain_token, mounted from /opt/aiadminpanel/secrets/managed_domain_token) rather than a plain env var, so the claim credential never lands in .env or docker inspect output. On a bring-your-own-domain install the installer still writes an empty, 0600 placeholder file so the compose mount never fails — the panel treats an empty token as "not managed" and skips all managed-domain behavior (no wizard step, no banner, no Settings card, no network calls to MANAGED_DNS_URL). On a claimed temporary domain, the panel — not the installer — owns verification and renewal from then on: see "Keeping your free domain" on the Installation page.
Authentication (Keycloak)
| Variable | Default | Description |
|---|---|---|
OIDC_ISSUER_URL | https://auth.{PANEL_DOMAIN}/realms/aiadminpanel | Keycloak realm URL |
OIDC_CLIENT_ID | panel | OIDC client identifier |
OIDC_CLIENT_SECRET | (auto-generated) | OIDC client secret |
SESSION_SECRET | (auto-generated) | Cookie encryption key |
Secrets Backend (OpenBao)
| Variable | Default | Description |
|---|---|---|
SECRETS_BACKEND | openbao | openbao (default) or file — file is the legacy escape hatch, disabling the secrets manager entirely |
BAO_ADDR | http://openbao:8200 | Internal address the panel uses to reach the bundled OpenBao service |
OPENBAO_VERSION | 2.5.5 | Pinned OpenBao image tag for the openbao compose service |
OpenBao stores AI provider API keys (platform and per-customer) in per-customer namespaces. See Secrets Manager for what it does and docs/runbooks/openbao.md in the repository for backup/restore/rotation procedures. All OpenBao failures are non-fatal to the panel: reads fall back to the legacy encrypted column, writes fail loudly with a localized error rather than silently succeeding.
SSL and Traefik
| Variable | Default | Description |
|---|---|---|
ACME_EMAIL | (required) | Email for Let's Encrypt notifications |
ACME_STORAGE | /traefik/acme.json | Certificate storage path |
ACME_CA_SERVER | https://acme-v02.api.letsencrypt.org/directory | ACME CA server URL |
Cloudflare DNS (Optional)
| Variable | Default | Description |
|---|---|---|
CF_DNS_API_TOKEN | (empty) | Cloudflare API token with DNS edit permission |
CF_ZONE_ID | (empty) | Cloudflare zone ID (auto-detected if token has zone read permission) |
CF_PROXIED | false | Whether to enable Cloudflare proxy on created DNS records |
AI Provider (Optional)
| Variable | Default | Description |
|---|---|---|
AI_PROVIDER | (empty) | AI provider: openai, litellm, ollama |
AI_API_URL | (empty) | AI API endpoint URL |
AI_API_KEY | (empty) | AI API key |
AI_MODEL | (empty) | Model name for AI-powered deployments |
In the setup wizard and in Settings → AI Providers, the model field is a curated dropdown of recent models for the selected provider (Anthropic / OpenAI / OpenRouter) — so you don't have to remember exact model ids. Pick one from the list, or choose Enter a custom model to type any other model name. The lists are refreshed each release.
Bundled Provider AI (Optional)
A hosting provider can pre-inject a central, OpenAI-compatible inference endpoint at install time so AI apps your customers deploy have a working upstream on first boot — with no key pasting. When AAP_PROVIDER_AI_API_KEY is present at install, the panel seeds a Provider AI source on first boot (encrypting the key into its own database — the key is never logged). It is always editable later in Settings → AI Providers, and the panel never overwrites an operator-edited value on a later reboot. Leave these unset to stay sovereign by default (no Provider AI configured).
| Variable | Default | Description |
|---|---|---|
AAP_PROVIDER_AI_API_KEY | (empty) | Bundles a central Provider AI source when set. Read once at first boot, encrypted into the panel DB. |
AAP_PROVIDER_AI_BASE_URL | (empty) | OpenAI-compatible base URL (e.g. https://inference.example.com/v1). |
AAP_PROVIDER_AI_MODEL | (empty) | Default model id for the bundled Provider AI. |
AAP_PROVIDER_AI_PROVIDER_TYPE | openai | openai (default) · openrouter · anthropic · ollama. |
Notifications (Optional)
| Variable | Default | Description |
|---|---|---|
SMTP_HOST | (empty) | SMTP server hostname |
SMTP_PORT | 587 | SMTP server port |
SMTP_USER | (empty) | SMTP username |
SMTP_PASS | (empty) | SMTP password |
SMTP_FROM | noreply@{PANEL_DOMAIN} | From address for email notifications |
Docker Compose Structure
The production deployment uses two compose files:
docker-compose.infra.yml — Infrastructure Services
Manages the foundational services that the panel depends on:
services:
postgresql:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- aiadminpanel
valkey:
image: valkey/valkey:8
networks:
- aiadminpanel
traefik:
image: traefik:v3.0
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/traefik
networks:
- aiadminpanel
keycloak:
image: quay.io/keycloak/keycloak:26.0
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`auth.${PANEL_DOMAIN}`)"
networks:
- aiadminpanel
openbao:
image: openbao/openbao:${OPENBAO_VERSION:-2.5.5}
volumes:
- openbao_data:/openbao/file
networks:
- aiadminpanel
# No Traefik labels — internal-only, no exposed route.
docker-compose.yml — Panel Application
Manages the panel itself:
services:
panel:
image: ghcr.io/aiadminpanel/panel:latest
env_file: .env
volumes:
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.routers.panel.rule=Host(`${PANEL_DOMAIN}`)"
- "traefik.http.routers.panel.tls.certresolver=${CERT_RESOLVER:-letsencrypt}"
networks:
- aiadminpanel
Keycloak Configuration
Keycloak serves as the sole identity provider. The installer creates:
- Realm:
aiadminpanel - Client:
panel(confidential, authorization code flow) - Admin user: created with the email and password from install
Keycloak Admin Console
Access at https://auth.{PANEL_DOMAIN}/admin/ with the Keycloak admin credentials.
Custom Branding
The panel applies dark branding to the Keycloak login page automatically. To customize further, modify the theme in the Keycloak admin console under Realm Settings > Themes.
Adding Users
Users can be created through:
- The panel's customer management UI (creates Keycloak user automatically)
- The Keycloak admin console directly
- The Keycloak user self-registration flow (if enabled)
Roles
| Role | Permissions |
|---|---|
admin | Full access to all resources and settings |
operator | Manage services, deploy, view all customers |
viewer | Read-only access to assigned resources |
Navigation adapts to your persona
The setup wizard asks how you'll use the panel (Just for me · For my clients · As a hosting business). On the Free and Power User editions your choice sets how much of the sidebar appears by default:
| Persona | Sidebar shows |
|---|---|
| Just for me | Services, Projects, Templates, AI Models, Insights, Settings |
| For my clients | the above + Customers, Plans, Roles, Audit Log |
On the Web Host edition the full reseller sidebar — the above + Resellers — is always shown, whatever persona you picked: activating the reseller tier reveals its features. (An edition upgrade to Web Host surfaces the reseller and customer views immediately; you don't need to change your persona.) On a lesser edition the hosting view isn't available — it falls back to the For my clients view.
The persona only sets the default depth on Free / Power User. To reveal everything your edition allows regardless of persona, switch on Advanced view from the user menu (top-right). Advanced view never locks anything away.
Switching context and finding your way
Once you manage customers, two extras keep you oriented:
- Context switcher — an Operator dropdown in the top toolbar. Use it to jump straight to a customer's page, or View as customer to open their portal in a new tab via single sign-on (no separate login). It appears only on the For my clients and As a hosting business views once you have at least one customer — a Just for me install never shows it.
- Breadcrumbs — a trail under the header (e.g. Operator › Customers › Acme Ltd) so you always know where you are and can click back up a level. It stays hidden on the dashboard and appears as you move into a section.
Traefik SSL Setup
Traefik handles all SSL termination via Let's Encrypt. The static configuration at /opt/aiadminpanel/traefik/traefik.yml:
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: "${ACME_EMAIL}"
storage: /traefik/acme.json
httpChallenge:
entryPoint: web
providers:
docker:
exposedByDefault: false
network: aiadminpanel
Services get SSL automatically when deployed — no manual certificate management required.
DNS-01 Certificates (Cloudflare)
For hosts with a closed inbound port 80 or a domain behind a proxy: by default the panel issues certificates with the HTTP-01 challenge, which needs inbound port 80 to be publicly reachable. On a host where port 80 is firewalled or the domain sits behind a proxy, use the DNS-01 challenge via Cloudflare instead — it proves domain ownership through a DNS record, so port 80 never has to be open (and it can issue wildcard *.panel.example.com certs).
Enabling it requires both of these in /opt/aiadminpanel/.env:
TLS_MODE=cloudflare # points the routers at the DNS-01 resolver CF_DNS_API_TOKEN=your-cf-token # Cloudflare token with Zone:DNS:Edit permission
TLS_MODE=cloudflareis the switch. Setting onlyCF_DNS_API_TOKENis not enough — withoutTLS_MODE=cloudflarethe panel and Keycloak routers keep using HTTP-01 and the DNS-01 resolver is never used. The installer writesCERT_RESOLVER=letsencrypt-dnsin cloudflare mode; a plain install leaves it unset, so the routers fall back toletsencrypt(HTTP-01) — unchanged.
Traefik already defines the DNS-01 resolver the routers switch to:
certificatesResolvers:
letsencrypt-dns:
acme:
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Cloudflare DNS Integration
When configured, the panel automatically creates DNS records for deployed services:
- Service deploys to
myapp.panel.example.com - Panel calls Cloudflare API to create an A record
- Traefik picks up the route and provisions SSL
- Service is accessible within seconds
Setting Up Cloudflare
- Go to your Cloudflare dashboard
- Create an API token with Zone:DNS:Edit permission for your domain
- Set
CF_DNS_API_TOKENin/opt/aiadminpanel/.env - Restart the panel:
docker compose restart panel
Without Cloudflare
If not using Cloudflare, you need a wildcard DNS record (*.panel.example.com) pointing to your server. Services will be accessible immediately after deploy since the wildcard catches all subdomains.