Afunana deploys as a set of containers managed by a single install script. One command installs everything on a fresh Linux server. The script auto-detects the container runtime (Docker or Podman) and uses whichever compose command is available — docker compose, podman-compose, or docker-compose. Throughout this guide, docker compose is shorthand for whichever of these is active on your host.
Prerequisites
Hardware Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 8 GB | 16 GB |
| CPU | 4 cores | 8 cores |
| Disk | 50 GB | 100 GB |
The installer checks available resources and warns if they fall below minimum thresholds. Warnings are informational only -- the installer never blocks on low resources.
Supported Operating Systems
| Distribution | Versions |
|---|---|
| Ubuntu | 22.04, 24.04 |
| Debian | 12 |
| AlmaLinux | 9.x |
| Rocky Linux | 9.x |
| RHEL | 9.x |
Software Requirements
curl(pre-installed on all supported distributions)- A container runtime — Docker or Podman. If neither is present, the setup script offers to install Docker CE automatically (this reaches
download.docker.comand needs internet). On RHEL/Podman hosts it uses the existing Podman +podman-compose. -
Air-gapped or RHEL/Podman hosts: install the runtime yourself before running setup.sh — the auto-install path fetches Docker over the internet and installs Docker (not Podman). On RHEL/AlmaLinux/Rocky:
(
podmanis in the default AppStream repo;podman-composecomes from EPEL, orpip3 install podman-composeif EPEL isn't mirrored internally.) Then run setup.sh — it detects Podman and proceeds.
Network Requirements
Afunana requires outbound internet access during installation and ongoing operation. The table below lists every external endpoint the system contacts.
Installation (one-time)
| Destination | Port | Purpose |
|---|---|---|
hub.docker.com / registry-1.docker.io |
443 | Pull the Afunana Docker image |
afunana.io |
443 | Download the setup script |
The embedding model (multilingual-e5) is pre-bundled inside the application image — no download is needed at runtime.
Runtime (ongoing)
| Destination | Port | Purpose | Required? |
|---|---|---|---|
api.anthropic.com |
443 | Anthropic Claude API (LLM calls) | If using Anthropic |
api.openai.com |
443 | OpenAI API (LLM calls) | If using OpenAI |
| Azure OpenAI endpoint | 443 | Azure OpenAI deployment (LLM calls) | If using Azure |
| Self-hosted Ollama | Configurable | Local LLM inference | If using Ollama |
| SQL Server (external mode) | 1433 (default) | Database connection to customer-supplied SQL Server | If DB_MODE=external |
| SMTP server | 587/465 | Outbound email (password resets, contact form) | Optional |
afunana.io |
443 | Version update check (once per hour, non-blocking) | Optional |
For the complete egress/ingress matrix when running with a customer-managed proxy and/or external database, see Network & Firewall Requirements.
Source Platform Integration (if applicable)
Afunana extracts source from the platform that runs the business. The endpoints depend on which platform a collection connects to (see Source Platform Integration).
| Platform | Destination | Port | Purpose |
|---|---|---|---|
| IBM i (AS/400) | IBM i host | 8471 | JDBC connection for source extraction |
| IBM i (AS/400) | IBM i host | 21 | FTP file transfer (SAVF upload/download) |
| Oracle | Oracle host | 1521 | Oracle Net (python-oracledb thin mode) — read-only data-dictionary + PL/SQL extraction |
| z/OS | — | — | Connection type defined; analysis on the near-term roadmap |
IBM i, Oracle, and plain SQL are all supported; Oracle is read-only (no Oracle client). z/OS is architected but analysis is not yet available.
Inbound
With the bundled Caddy reverse proxy (default):
| Port | Purpose |
|---|---|
| 80 | HTTP (redirects to HTTPS) |
| 443 | HTTPS — web UI, API, VS Code extension |
With a customer-managed proxy (nginx/F5/HAProxy), the app instead listens on host port 18080 and the customer's proxy fronts it. The app binds 127.0.0.1:18080 when the proxy is on the same host, or 0.0.0.0:18080 (firewall-restricted) when the proxy is on a separate host. See Reverse Proxy (Customer-Managed).
The bundled SQL Server (when used) publishes no host ports — it is reachable only on the internal container network.
Air-Gapped / Restricted Networks
Afunana can run in networks that block general internet access, provided:
- A container runtime is installed beforehand (
dnf install -y podman podman-composefrom your internal repos) — the setup script's auto-install reachesdownload.docker.comand will not work offline - The images are loaded from a local file or an internal registry instead of pulling from Docker Hub (see Offline / Air-Gapped Delivery below)
- At least one LLM provider is reachable — either a cloud API (Anthropic/OpenAI/Azure) or a local Ollama instance on the same network
- The embedding model does not require internet — it ships inside the application image
If your network blocks all outbound HTTPS, use Ollama on a local server and load the images from the offline delivery tarball.
Where Images Come From
The installer can obtain the application, SQL Server, and Caddy images from any of three sources:
- Docker Hub (default). The Afunana application image is private (
afunana/afunana-app:latest;dovkis the Docker Hub login user, not the repository). You need a Docker Hub access token; the installer prompts for it. Contact your Afunana representative to obtain a token. - An internal registry. Set
DOCKER_REPO,MSSQL_IMAGE, and/orCADDY_IMAGE(env vars or prompts) to point at a mirror — e.g.MSSQL_IMAGE=registry.internal/mssql/server:2022-latest. No Docker Hub access is needed. - An offline tarball. Produced by
scripts/make-delivery.shand loaded automatically bysetup.sh— see Offline / Air-Gapped Delivery.
Quick Install
Option A: Online Install
Run this single command on a fresh server:
Option B: Local Install (no access to afunana.io)
For restricted environments (e.g., client data centers), copy the setup script to the server and run it directly:
The script still needs access to Docker Hub (or an internal registry) to pull the images. If both are blocked, use the offline tarball described in Offline / Air-Gapped Delivery.
Both options run the same script. It prompts for your Docker Hub token (unless the images are already loaded or come from an internal registry) and displays generated credentials when finished.
Interactive Prompts
The installer can run two ways: interactively, asking a short set of choices up front, or fully unattended from an answers file with zero prompts.
Interactively it asks only a handful of things: the database mode (bundled SQL Server, or an external/customer-supplied SQL Server — for which it also collects host/port/login and runs a reachability pre-flight); how the app is reached on the network; the public URL and organization name; and the Docker Hub token (only if images are not already loaded and no internal registry is configured). "How the app is reached" is one of three shapes:
- Built-in Caddy — the installer runs a Caddy container for automatic TLS. Default; best when Afunana manages everything and a domain/internet is available.
- No proxy — the app serves itself — the app is its own web server: it serves the screens and the API on one port and handles the
/apiprefix internally. Front it with nothing (openhttp://host:port) or with any TLS device / load-balancer of the customer's — that device just forwards to the port, with no/apirules, no path rewriting required. This is the recommended mode when the customer runs their own edge or wants a plain internal deployment. - Customer-managed proxy that strips
/api— legacy: the customer's nginx/F5 terminates TLS and strips the/apiprefix.
Unattended (Answers-File) Install
For repeatable, hands-off rollouts the installer reads an answers file — afunana-setup.conf — that pre-supplies every choice it would otherwise prompt for. Point the installer at it (via the AFUNANA_ANSWERS path) and it runs in zero-prompt mode: no questions, suitable for automation. Before it changes anything it shows a pre-install review screen summarizing the resolved settings (database mode, how the app is reached, public URL, image source) so the operator can confirm the plan.
For an external database, the answers file also captures how the DB password is obtained: it can be pulled at runtime from a vault (CyberArk CCP), or the app can generate and write its own DB secret. An AlwaysOn availability-group listener can be configured here too, so Afunana connects through the AG listener and rides SQL Server failover.
Offline / Air-Gapped Delivery
For environments with no Docker Hub or internal-registry access, build a self-contained delivery tarball on a connected machine with scripts/make-delivery.sh. It produces:
afunana-app-<ver>.tar— the application image alone, orafunana-bundle-<ver>.tar— the application image plus SQL Server and Caddy (for fully air-gapped bundled-DB installs).
Copy the tarball and setup.sh to the target server in the same directory. On startup setup.sh auto-detects and loads afunana-app-<ver>.tar / afunana-bundle-<ver>.tar, then continues the normal install with no registry access required.
What the Installer Does
The setup script runs seven labeled steps. Alongside those steps it also configures security and operational defaults automatically — TDE encryption, a least-privilege database user, daily backups, log rotation, the public URL, and the admin user.
- Install Docker — detects the OS, checks hardware resources (warns only, never blocks), and installs Docker + Docker Compose if no runtime is present. On Podman hosts it uses the existing Podman +
podman-compose. - Pull image — authenticates and pulls the application (and, for bundled mode, SQL Server / Caddy) images from Docker Hub, an internal registry, or a pre-loaded offline tarball.
- Create configuration — creates
/opt/afunana, generatesdocker-compose.yml(with only the services the chosen modes need), writes secrets and the bootstrapbackend/.env+backend/seed.env, and seedsFRONTEND_URL. Random passwords are generated for the SA account, the application DB login, JWT signing, the deploy secret, and the TDE certificate. - Start services — brings up the database (bundled mode) and waits for it to become reachable.
- Create database & app user — bundled mode creates the
afunanadatabase; external mode verifies reachability and saves the schema SQL to/opt/afunana/db/for the customer's DBA. The application then self-seeds its schema on first boot. - Security hardening (automatic) — creates a least-privilege
afunana_appdatabase login (not SA), enables AES-256 TDE encryption at rest and backs up the TDE certificate to/opt/afunana/tde-cert-backup/, installs a daily backup cron (02:00 UTC, 7-day retention), and configures log rotation. The admin user is seeded fromADMIN_SEED_USER/ADMIN_SEED_PASSWORD. (For external SQL Server, database hardening and backups are the customer's responsibility and this step is skipped with a reminder.) - Wait for application — starts the app (and Caddy, if in Caddy mode, via
--profile caddy), pollshttp://localhost:18080/healthuntil healthy, and displays the admin credentials and generated secrets.
Existing Installation Detection
If Afunana is already installed, the script detects the existing installation and offers options to update, reinstall, or reconfigure.
Services Created
The set of containers depends on the chosen modes:
| Service | Description | Present when | Ports |
|---|---|---|---|
| init-frontend | One-shot job that copies the built frontend into a shared volume | Always (runs once, then exits) | none |
| afunana-app | FastAPI application server | Always | host 18080 → container 8001 |
| caddy | Reverse proxy with automatic TLS | Only in Caddy proxy mode (profile-gated) | 80, 443 |
| sqlserver | SQL Server 2022 database | Only in bundled-DB mode | none (internal network only) |
The app's container-internal port is 8001; on the host it is published as 18080 (bound to localhost in Caddy mode, or to the configured address in customer-proxy mode). The bundled SQL Server (image mcr.microsoft.com/mssql/server:2022-latest) runs only on the internal container network and publishes no host ports.
Secrets
The installer generates the application's secrets automatically (DB login, SA password, JWT signing key, deploy secret, TDE certificate password, and empty placeholders for the API/IBM i keys) and stores them as chmod 600 files, mounted into the containers. Do not delete or modify them unless you understand the implications.
First Login
After installation completes, open a browser and navigate to your server's IP address or hostname.
Default credentials:
| Field | Value |
|---|---|
| Username | The seeded admin username (ADMIN_SEED_USER, defaults to admin) |
| Password | Displayed in the terminal during installation, or set via ADMIN_SEED_PASSWORD env var |
Change the admin password immediately after first login.
Initial Configuration
After logging in, navigate to Admin > Settings > Configuration to complete setup.
Step 1: AI Provider Keys
At least one AI provider key is required for analysis and chat features.
| Key | Provider |
|---|---|
OPENAI_API_KEY |
OpenAI |
ANTHROPIC_API_KEY |
Anthropic |
AZURE_API_KEY / AZURE_API_BASE / AZURE_API_VERSION |
Azure OpenAI |
Enter the key in the Configuration page, or write it directly to the secrets file and restart the app container.
Step 2: Source Platform Connection (if applicable)
Source connections are defined on the Connections screen (Admin > Connections), not in the flat configuration table. Create a connection of the appropriate type:
- IBM i (AS/400) — host, user, password, and JDBC port (default
8471); read-only extraction via the server-side TTDOC component. - Oracle — host, port (default
1521), service name, username/password, and schema; read-only extraction of the data dictionary and PL/SQL, no Oracle client required. - z/OS — connection type is defined; source analysis is on the near-term roadmap.
A collection derives its platform from the connection it is bound to. Connection passwords are stored server-side, encrypted, never exposed in the UI.
See the Source Platform Integration guide for the full per-platform reference, the IBM i component install, authorities, and the extraction flow.
Step 3: Organization Settings
| Key | Description |
|---|---|
ORG_NAME |
Your organization name (shown in the UI) |
FRONTEND_URL |
Public URL of the Afunana instance |
Set FRONTEND_URL to the full URL users will access (e.g., https://afunana.example.com). This is used for CORS and link generation. The system also checks the Host header and environment variables as fallbacks, but setting it explicitly is recommended.
Verify Installation
Check that all services are running.
The services for your chosen modes (always afunana-app; plus sqlserver in bundled-DB mode and caddy in Caddy mode) should show a status of Up. init-frontend runs once and exits — that is expected.
Check application health on the host port:
A successful response confirms the application is running and connected to the database. (Port 8001 is container-internal only; from the host use 18080.)
Check container logs if something is not working.
Troubleshooting
| Symptom | Check |
|---|---|
| Docker Hub authentication fails | Verify your token is correct. Re-run setup.sh and re-enter the token. |
| Containers fail to start | Check disk space and RAM. SQL Server requires at least 2 GB RAM. Review the container logs. |
| Cannot access the web interface | In bundled-proxy mode, verify the web ports are open and the proxy container is running. In customer-proxy mode, verify the proxy is up and the app responds on its health endpoint. |
| Admin password not working | Set the admin seed password and restart the containers. |
| 502 Bad Gateway | The backend may still be starting. Check the application container's logs. |
| Database connection error | Verify the database container is running. |