Installation
AI Admin Panel installs on a fresh Linux server and sets up everything: Docker, PostgreSQL, Valkey, Traefik (auto-SSL), Keycloak (OIDC auth), OpenBao (secrets manager), and the panel itself.
Prerequisites
Server Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4+ cores |
| RAM | 4 GB | 8+ GB |
| Disk | 20 GB SSD | 50+ GB SSD |
| OS | Any certified Linux distro (see below) | Ubuntu 24.04 LTS / Rocky Linux 9 |
| Architecture | x86_64 (amd64), aarch64 (arm64) | x86_64 |
Supported Linux Distributions
The installer detects your distribution by package manager + version floor (not a fixed allowlist), so newer minor/point releases of a supported family work automatically. It requires systemd (Docker is managed via systemctl) and rootful Docker on a 64-bit host; on RHEL-family distros it installs Docker from Docker's official repository and handles SELinux (:z labels) and firewalld automatically.
| Tier | Distributions | Behaviour |
|---|---|---|
| Certified — tested in CI on every release | Ubuntu 22.04 / 24.04 / 26.04 LTS · Debian 12 / 13 · Rocky Linux 9 / 10 · AlmaLinux 9 / 10 · RHEL 9 / 10 · CentOS Stream 9 / 10 | Fully supported |
| Best-effort | Fedora (current) · Oracle Linux 8 / 9 / 10 · Amazon Linux 2023 | Installs and runs; warns it isn't in the certified matrix |
| Community | openSUSE Leap / SLES · Arch Linux | Detected; best-effort (SUSE has no official Docker repo, so Docker may need installing first) |
| Not supported | Alpine / OpenRC (no systemd) · EOL releases (Ubuntu ≤ 20.04, Debian ≤ 11, CentOS Linux 7 / 8, Amazon Linux 2) · 32-bit · OpenVZ / unprivileged LXC | Installer stops with a clear message naming what is supported |
DNS Requirements
No domain? You don't need one. Leave the installer's domain prompt empty (or run unattended without
PANEL_DOMAIN) and it claims a free temporary domain for you — likeaap-x7k2f9.aiadminpanel.host— points it at your server, and gives the panel, login, and every deployed service working HTTPS automatically. No DNS records to create, no terminal. Verify your email in the panel afterwards to keep it, or disable the whole path withMANAGED_DOMAIN=off. Full details in "No Domain? Get a Free Temporary One" below.
Bringing your own domain? Point it to the server before installing:
- A record —
panel.example.com→ your server IP - Wildcard A record —
*.panel.example.com→ same server IP
The wildcard record is essential — every deployed service gets a subdomain like myapp.panel.example.com.
Tip: Cloudflare Users — Disable the orange proxy cloud during installation so Let's Encrypt can issue certificates via the HTTP challenge. Re-enable it after setup if desired. Alternatively, install with
TLS_MODE=cloudflare+CF_DNS_API_TOKENto use the DNS-01 challenge — that needs no inbound port 80, so you can leave the proxy on (see Configuration → DNS-01 Certificates).
Network Requirements
| Port | Direction | Purpose |
|---|---|---|
| 80 | Inbound | HTTP → auto-redirects to HTTPS |
| 443 | Inbound | HTTPS (Traefik terminates SSL) |
| 22 | Inbound | SSH access |
No other services should be running on ports 80/443.
Automated Install
curl -fsSL https://get.aiadminpanel.com -o install.sh && bash install.sh
When you open a fresh setup wizard, it starts in English. If your browser uses another supported language, the first step offers a switch using that language's native name, or Keep English. Either choice is remembered through later steps and reloads. You can also use the language menu at any time. Existing saved language preferences are preserved; unsupported browser languages fall back to English. Browser detection outside onboarding still works, but only an explicit language choice is saved. If browser storage is blocked, the choice lasts for the current page session and cannot be remembered after a reload.
If the wizard shows a hostname or admin-account validation error, correct the field and the message updates while you type. You can then click Next once. Password confirmation is checked again when either password field changes, and visible validation messages follow your selected language.
The installer asks for your domain and creates the initial panel administrator in Keycloak: admin@{PANEL_DOMAIN}. It generates a random password unless you supply ADMIN_PASSWORD, then prints the panel URL, administrator email, and password. Save them in your password manager; keep the terminal output private. The panel password is also stored on the server at /opt/aiadminpanel/secrets/admin_password.
On the standard installer path, Keycloak is already configured, so the setup wizard skips its local administrator-account step. The contact email you enter during setup is not a replacement login. Complete the edition, contact/legal, and AI configuration steps, then use Sign In with the installer-created account. The Keycloak management-console account is separate.
Open the admin URL to complete setup. On desktop, the progress list sits beside the form; on narrow screens it sits above it. Long translated step names wrap, and the current step's description appears below the heading. Use the language switch above the logo to choose a supported language. Longer steps scroll vertically so all fields and the Back/Continue actions remain reachable.
To skip the prompt (CI/automation), set environment variables:
curl -fsSL https://get.aiadminpanel.com -o install.sh PANEL_DOMAIN="panel.example.com" ACME_EMAIL="admin@example.com" \ bash install.sh --unattended
Installer Options
| Flag | Description |
|---|---|
--dry-run | Preview all actions without executing |
--verbose | Enable debug output |
--unattended | Non-interactive mode (claims a free temporary domain when PANEL_DOMAIN is unset; set MANAGED_DOMAIN=off to require PANEL_DOMAIN) |
Environment Variables
| Variable | Required | Description |
|---|---|---|
PANEL_DOMAIN | No | Base domain (e.g., panel.example.com). Leave unset to claim a free temporary domain like aap-x7k2f9.aiadminpanel.host |
MANAGED_DOMAIN | No | Set to off to disable the temporary-domain claim path (installs then require PANEL_DOMAIN) |
MANAGED_DNS_URL | No | Claim service base URL (default https://dns.aiadminpanel.com) |
ACME_EMAIL | No | Email for Let's Encrypt (default: admin@example.com) |
CF_DNS_API_TOKEN | No | Cloudflare API token for wildcard certs and auto-DNS |
PANEL_VERSION | No | Version to install (default: latest) |
GOMEMLIMIT | No | Go soft memory limit: use off for no optional limit or an explicit budget; defaults to off (0 means zero bytes) |
TLS_MODE | No | letsencrypt (default) or cloudflare (DNS-01; skips the public A-record pre-flight check) |
AAP_FIX_ROUTING | No | Set to 1 to auto-fix a detected dual-default-route hazard on multi-homed VPSes (OVH/Hetzner). netplan hosts only; otherwise the installer just warns |
ADMIN_PASSWORD | No | Override the auto-generated admin password |
LICENSE_KEY | No | License key for activation during setup (omit to choose Free or a 14-day paid-edition trial) |
OPENBAO_VERSION | No | Pinned OpenBao secrets-manager image tag (default: 2.5.5) |
SECRETS_BACKEND | No | openbao (default) or file — file disables the secrets manager entirely and falls back to the legacy encrypted-column storage |
BAO_ADDR | No | Internal address the panel uses to reach OpenBao (default: http://openbao:8200) — only needed for non-standard topologies |
LITELLM_MASTER_KEY | No | Master key for the bundled LiteLLM AI gateway. The installer auto-generates a random per-install key — you don't normally set this. There is no shared default: a manual/advanced install that runs docker compose directly must provide it (the panel + gateway containers fail to start with a clear error otherwise) |
No Domain? Get a Free Temporary One
If you don't have a domain yet, just leave the domain prompt empty (or run unattended without PANEL_DOMAIN): the installer claims a free temporary domain like aap-x7k2f9.aiadminpanel.host, points it at your server, and the panel, login, and every deployed service get working HTTPS under it automatically.
Temporary domains start with a 7-day provisional lease — verify your email in the panel to keep the domain (it then renews automatically while your panel is running). The claim credential is stored at /opt/aiadminpanel/secrets/managed_domain_token (root-only); don't delete it. Temporary domains are IPv4-only, limited to 3 claims per IP per day, and can be disabled entirely with MANAGED_DOMAIN=off.
Waiting for your temporary domain
The installer waits for a new or reused temporary domain to resolve to this server's public IPv4. It checks up to 12 times with five-second retry delays (up to one minute of delays, plus lookup time). An answer pointing at another server does not count as ready. If DNS still points elsewhere, the preflight check stops installation before certificate requests. If the public-IP lookup is unavailable, the installer warns that readiness is unconfirmed; preflight retries that lookup and retains its existing warning-only behavior if it remains unavailable.
Keep /opt/aiadminpanel/secrets/managed_domain_token and rerun the installer with the domain prompt empty once DNS has settled. A valid saved claim reuses the same domain without consuming another claim. Do not delete the token or bypass the DNS check. TLS_MODE=cloudflare continues to skip this A-record check for DNS-01 certificates.
Keeping your free domain
Finish the wizard with Sign In (or Complete Setup) to save your setup and activate the selected edition. Verifying the domain email is a separate step; it does not activate the panel licence. If the final save fails, the wizard displays an error and keeps your entered values so you can retry. A completed installation routes signed-out visitors through login; an installation still awaiting activation continues to require setup.
Once the install finishes, the panel itself takes over the domain's lifecycle — no terminal needed. Verify your email in either of two places:
- The setup wizard's "Keep your free domain" step (second-to-last step, and skippable if you'd rather decide later).
- Settings → DNS & Domains, in the "Your free domain" card: shows the domain, a verified/unverified badge, the lease expiry, the last renewal result, and a send/resend-verification-email form. A Renew now button is always available there too.
During setup, the verification form reuses your Contact email (or your admin-account email if no usable contact address is available). You can correct it before sending. An explicit correction takes precedence if you later change the Contact email. These email fields are kept for the current browser tab across navigation and reload, and cleared after successful setup; passwords, licence keys and AI keys are not part of this draft. If browser storage is unavailable, the current wizard still works but a reload cannot retain the email correction. Prefilling or reloading never sends an email or proves verification: use the send button, then follow the email link.
Click the email link to open your panel's verification return page. It checks the server before confirming verification. If the check cannot confirm it, choose Check again; a URL marker alone never confirms verification.
After setup is complete, choose Sign in or Open panel. If setup is still in progress, return to the original setup tab: it updates automatically and retains your entered details. Keep that tab open while checking email. If it was closed or reloaded, choose Continue setup and enter missing details again; license and AI keys are not saved in browser storage. Invalid, expired or already-used email links show an explicit unavailable-link page instead of a success redirect.
Verified domains receive a 60-day rolling lease that renews itself automatically. A background job checks the lease daily, renews it on a weekly cadence while things are healthy, and retries daily if a renewal attempt fails.
If you haven't verified yet, a banner appears at the top of the panel and escalates as the 7-day grace period runs out: a routine reminder to verify, then an urgent one once you're down to the final 2 days. After you've verified, the same banner reappears only if auto-renewal starts failing and the lease drops under 14 days, and it flags the domain outright if the lease ever lapses. No banner at all means everything is healthy.
If the lease lapses — verification was skipped past the deadline, or renewal kept failing until the claim expired — the panel keeps running (you don't lose access or data), but the temporary domain stops resolving, so the panel, login, and any services published on that domain become unreachable at that address. A lapsed claim doesn't renew itself again; add a domain you own under Settings → DNS & Domains to recover.
Bring-your-own-domain installs never see any of this — no wizard step, no banner, no Settings card, and no extra network calls.
What the Installer Creates
/opt/aiadminpanel/
├── .env # Environment configuration
├── docker-compose.yml # Production compose file
├── keycloak-realm-export.json # Keycloak realm with panel clients
├── litellm-config.yaml # LiteLLM AI gateway config
├── secrets/ # Secret files — persistent, survive reboot
│ ├── master_key # Panel encryption key
│ ├── db_password # PostgreSQL password
│ ├── keycloak_admin_password # Keycloak admin password
│ ├── openbao_unseal_key # OpenBao secrets-manager auto-unseal key
│ └── admin_password # Panel admin password
└── letsencrypt/
├── acme.json # Let's Encrypt certificates
└── acme-dns.json # Wildcard certificates (if Cloudflare)
/etc/aiadminpanel/
└── config.yaml # Panel configuration
/var/log/aiadminpanel/
└── install.log # Installation log
Secret files are mounted into the containers at
/run/secrets/<name>by Docker, but their source of truth lives in/opt/aiadminpanel/secrets/so they survive a host reboot.
What Gets Deployed
The install creates these Docker containers:
| Container | Image | Purpose |
|---|---|---|
aiadminpanel_traefik | traefik:v3 | Reverse proxy, auto-SSL |
aiadminpanel_postgresql | postgres:16-alpine | Primary database |
aiadminpanel_valkey | valkey/valkey:8-alpine | Cache and pub/sub |
aiadminpanel_keycloak | keycloak:26.0 | OIDC identity provider |
aiadminpanel_openbao | openbao/openbao:2.5.5 | Secrets manager (AI provider keys) — internal only, no exposed port |
aiadminpanel_panel | ghcr.io/aiadminpanel/ai-admin-panel | The panel itself |
aiadminpanel_ollama | ollama/ollama | Local LLM inference |
aiadminpanel_litellm | ghcr.io/berriai/litellm | AI gateway |
GPU Support (NVIDIA)
If the server has an NVIDIA GPU, the installer detects it during setup and automatically installs and configures the NVIDIA Container Toolkit plus Docker's nvidia runtime — no terminal steps. Once that's done, AI services you deploy with GPU access use the card, and the instance detail under AI Models in the panel shows the model, VRAM, driver, and live utilization.
The bundled Local AI (Ollama) is GPU-accelerated automatically too. On a GPU host the installer reserves the card for the bundled Ollama, so Local AI uses the GPU out of the box and the AI Models page shows a ⚡ GPU badge for it — no deploy step or terminal needed. On a CPU-only host Ollama stays on the CPU and nothing changes. Already running on a GPU box? Just re-run the installer and the bundled Ollama picks up the GPU.
This runs on the apt (Ubuntu/Debian), dnf (RHEL/Rocky/AlmaLinux/Fedora), and zypper (SUSE) families. It's idempotent — re-running the installer just re-asserts the configuration — and a complete no-op on CPU-only hosts.
GPU driver is a prerequisite. The installer sets up the container plumbing, but the NVIDIA kernel driver must already be present. Many bare cloud GPU images ship without one. If the installer warns that a GPU is present but no driver is loaded, install it and reboot:
- Ubuntu/Debian:
sudo ubuntu-drivers install(orsudo apt install -y nvidia-driver-<version>)- RHEL/Rocky/AlmaLinux: enable EPEL + NVIDIA's CUDA repo, then
sudo dnf install -y nvidia-driverVerify with
nvidia-smi, then re-run the panel installer to finish the toolkit configuration.
AMD/ROCm GPUs are not yet auto-configured by the installer.
Post-Install Verification
- Open the panel URL printed by the installer. Complete setup if it appears, then sign in through Keycloak using the installer-created administrator account.
- Confirm the dashboard opens. Open Templates, search for Uptime Kuma, and check that its detail page offers Deploy. A Coming Soon card is not ready to deploy; the unfiltered catalog is not a complete inventory in this version.
- Open Settings → Security & License → License and check the selected edition or trial.
- Follow First Deploy to verify an application can start and open through its displayed URL.
If the browser cannot reach the panel, these optional server checks help narrow the problem. They do not prove that login or deployment works:
curl --fail --silent --show-error https://panel.example.com/healthz cd /opt/aiadminpanel docker compose ps
Administrator access
Use admin@{PANEL_DOMAIN} and the generated panel password for the first panel login. Use the separate Keycloak administrator account only for identity-provider administration at https://auth.{PANEL_DOMAIN}/admin/; its password is stored in /opt/aiadminpanel/secrets/keycloak_admin_password. Neither password has a shared default. Do not paste either secret into support tickets or screenshots.
Troubleshooting
Port 80/443 already in use
Identify the service using those ports before proceeding. Use a fresh server, or plan how to move the existing web server and its sites; stopping it may interrupt other applications.
Panel unreachable in a browser, but SSH works (multi-homed VPS)
On OVH and Hetzner Cloud with a private network, the private interface can install a default route at the same priority as the public one. Host traffic (SSH, curl) still works, but Docker container replies exit the private interface and are dropped — so the panel times out in a browser and containers can't reach the internet. This is not a firewall issue. See Multi-Homed VPS Routing for the one-file fix, or re-run the installer with AAP_FIX_ROUTING=1 to apply it automatically.
DNS not resolving
dig +short panel.example.com dig +short test.panel.example.com
Both should return your server IP. If not, wait for DNS propagation (up to 48 hours for some providers).
Let's Encrypt rate limits
For testing, use the staging CA:
# In docker-compose.yml, change the ACME server to: # --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
Production rate limit: 50 certificates per registered domain per week.
Keycloak not starting
Keycloak needs its own database. If it fails to start:
# Check logs docker logs aiadminpanel_keycloak # Common fix: create the database docker exec aiadminpanel_postgresql psql -U aiadminpanel -c "CREATE DATABASE keycloak;" docker compose restart keycloak
Host updater release assets
The corrected host updater reads the compose file and missing host files from the selected release's versioned installer assets. For example, image version 2.12.2 uses the v2.12.2/ asset prefix. The image version remains unchanged for signature verification and image selection. Existing host files and the GPU overlay are preserved, and the previous compose remains available for automatic rollback.
This correction shipped in 2.12.3 and is present in the 2.12.8 source baseline. Existing installations need an operator-managed refresh of /opt/aiadminpanel/aap-update.sh; replacing the panel image does not replace that host script. A release without versioned compose assets still receives an image-only update, so check the updater log when confirming a compose-level fix.
Panel CPU usage and Go memory limits
The installer defaults the optional GOMEMLIMIT setting to off. Go interprets 0 as a zero-byte soft memory limit, which creates continuous garbage-collection pressure. Use off for Go's normal behavior or an explicit memory budget sized for your installation. A Go soft limit is not a Docker container memory limit.
For an existing installation with GOMEMLIMIT=0, back up /opt/aiadminpanel/.env, change only that line to GOMEMLIMIT=off (or your chosen budget), then recreate the panel from the installation directory:
cd /opt/aiadminpanel docker compose up -d --no-deps --force-recreate panel
This briefly interrupts panel access. The installer preserves existing .env files, so rerunning it does not change this setting. Check resource usage again after recreation; this correction does not establish the cause of every memory growth or out-of-memory failure.
Panel 2.12.2 also fixed accumulating OpenBao token-renewal watchers. That repair requires the updated panel binary; changing GOMEMLIMIT alone does not fix the watcher leak. Existing secrets and tenant namespaces are preserved.
Checking logs
docker logs --tail 100 aiadminpanel_panel # Panel docker logs --tail 100 aiadminpanel_traefik # Traefik docker logs --tail 100 aiadminpanel_keycloak # Keycloak docker logs --tail 100 aiadminpanel_postgresql # PostgreSQL
Dry run
Test the installer without making changes:
bash install.sh --dry-run --verbose
AI gateway is running but tenant keys fail
If creating an AI gateway key returns gateway_unavailable, inspect the LiteLLM container's readiness, including its database connection. Version 2.12.2 uses /health/readiness and requires db=connected for Docker health; older releases only check whether the HTTP process responds. A healthy status on those older releases does not establish that key management works.
After checking the database is available, restart only the gateway with docker compose restart litellm from the installation directory and retry key creation. A restart may recover a stale connection; repeated failures require investigation of LiteLLM and PostgreSQL logs. Do not rotate credentials merely because the gateway reports a disconnected database.
Access to SSO-protected applications
SSO-protected application domains require both a valid panel session and access to the service. Operators can access managed services. Customers and invited members can access services belonging to their active customer account; other customers receive HTTP 403. Missing or expired sessions receive HTTP 401.
If a hostname matches more than one service, all users, including operators, receive HTTP 403. This also covers a custom domain matching another service’s generated hostname or a domain differing only by letter case. Correct the conflicting domain assignment before retrying; a valid login cannot disambiguate which application should receive the request.
This ownership check is included in version 2.12.2. Installations on earlier versions must not treat a valid panel login as proof that a user owns the application. Keep application ports behind the configured proxy; this HTTP authorization check does not provide isolation for direct connections between containers.
Application rejects its browser origin after reassignment
Version 2.12.2 restores deployment host variables before rebuilding a service for customer reassignment or secret-driven recreation. This keeps OpenClaw's allowed browser origin equal to its actual HTTPS domain. Existing affected containers need recreation with the fixed panel; restarting the old container alone retains its old command. Keep the original named volumes and credentials. Do not work around the failure with wildcard origins.
Subdomain renaming uses a separate lifecycle path and is not certified by this repair; its configuration-preservation work is tracked separately.
OpenClaw selects a cloud model with a local gateway key
The updated OpenClaw template configures the selected OpenAI-compatible source as the application's aap provider and selects aap/<model> for new sessions. It supplies the endpoint, model and an environment-backed credential together. It does not place a gateway credential in the built-in OpenAI provider.
Startup refreshes the endpoint, key and model list inside models.providers.aap and selects agents.defaults.model with no model fallbacks. Other provider options, including an operator-configured timeout, and other application settings are preserved. An incomplete source with no endpoint or model stops startup instead of silently choosing a cloud model. Direct Anthropic and manually entered provider keys retain their existing configuration flow.
Existing services retain their stored Compose command; restarting an old service does not install a new template. Use a fresh deployment for this correction or perform a separately reviewed configuration migration with a volume backup. Existing chat sessions can retain their previous model selection; create a new session and check its model before sending a prompt. Local inference quality, context capacity and latency need a real task on the chosen model and hardware.