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
The installer will ask for your domain, then handle everything else automatically. At the end it prints your admin URL, email, and password.
To skip the prompt (CI/automation), set environment variables:
PANEL_DOMAIN="panel.example.com" ACME_EMAIL="[email protected]" \ curl -fsSL https://get.aiadminpanel.com | bash
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: [email protected]) |
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 memory limit (default: 0 = unlimited) |
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 (omit for a 14-day 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.
Keeping your free domain
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.
Click the link in the verification email and you're taken straight back into your panel's Settings → DNS & Domains page (a short "email confirmed" message appears and the "Your free domain" card flips to verified) — the lease stops being provisional and becomes 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 Hardware → GPUs 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
# Check panel health
curl -s https://panel.example.com/healthz | jq .
# Check Keycloak is accessible
curl -s -o /dev/null -w "%{http_code}" https://auth.panel.example.com
# Check template catalog loaded
curl -s https://panel.example.com/api/v1/templates/catalog | jq '.templates | length'
# Expected: 45
# Check all containers running
docker compose -f /opt/aiadminpanel/docker-compose.yml ps
Then open https://panel.example.com and log in with the admin email and password the installer printed at the end (the password is also saved on the server at /opt/aiadminpanel/secrets/admin_password).
Default Credentials
| Service | URL | Username | Password |
|---|---|---|---|
| Keycloak Admin | https://auth.{domain}/admin/ | admin | Contents of /opt/aiadminpanel/secrets/keycloak_admin_password |
| Panel | https://{domain} | Created via Keycloak | — |
Troubleshooting
Port 80/443 already in use
Stop any existing web server:
systemctl stop nginx apache2 2>/dev/null systemctl disable nginx apache2 2>/dev/null
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
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