On-Premises Installation
This guide covers getting the deployment files, configuring the .env file, installing Docker, and starting the Authnull On-Premises stack.
Step 1: Get the Deployment Files
Clone the deployment repository authnull-onprem-public on the server:
git clone https://github.com/authnull0/authnull-onprem-public.git
cd authnull-onprem-public
The repository contains:
| File | Purpose |
|---|---|
docker-compose.yml | The stack definition |
.env | Configuration read by Docker Compose |
nginx/nginx.conf | Nginx configuration for a domain with HTTPS |
nginx/nginx-ip.conf | Nginx configuration for a bare IP address over HTTP |
db-init/ | Database schema and migrations, applied automatically on start |
Step 2: Configure Environment Variables
Open .env. Only the values marked CUSTOMER need to be set; everything else is preset.
Database credentials
These are the credentials for the PostgreSQL container the stack creates. Choose any user and password; each pair must be identical.
DB_USER=authnull
DB_PASSWORD=choose-a-strong-password
POSTGRES_USER=authnull
POSTGRES_PASSWORD=choose-a-strong-password
Organization
TENANT_ID=1
ORG_NAME=customer-org-name
Access mode
Uncomment one of the two blocks in .env, and replace the placeholders. URLs must not end with a slash.
A) Domain with HTTPS - use with nginx/nginx.conf. Needs DNS records and certificates (see Network Access).
SYSTEM_URL=https://onprem.customer-domain.com
SYSTEM_IP=SERVER_IP
DOMAIN_URL=default.customer-org-name.customer-domain.com
SSC_SIGNIN_URL=https://onprem.customer-domain.com/ssc/signin
SSC_BASE_URL=https://onprem.customer-domain.com
CLIENT_URL=https://onprem.customer-domain.com
TENANT_BASE_DOMAIN=customer-domain.com
WEBAUTHN_RP_ID=customer-domain.com
WEBAUTHN_ORIGIN=https://onprem.customer-domain.com,https://default.customer-org-name.customer-domain.com
ROOT_URL=https://onprem.customer-domain.com
SSC_URL=https://onprem.customer-domain.com/ssc/signin
AUTHNULL_LOGOUT_URL=https://onprem.customer-domain.com/ssc/signin
B) Bare IP over HTTP - use with nginx/nginx-ip.conf. No DNS or certificate needed.
SYSTEM_URL=http://SERVER_IP
SYSTEM_IP=SERVER_IP
DOMAIN_URL=default.customer-org-name.customer-domain.com
SSC_SIGNIN_URL=http://SERVER_IP/ssc/signin
SSC_BASE_URL=http://SERVER_IP
CLIENT_URL=http://SERVER_IP
TENANT_BASE_DOMAIN=customer-domain.com
WEBAUTHN_RP_ID=SERVER_IP
WEBAUTHN_ORIGIN=http://SERVER_IP
ROOT_URL=http://SERVER_IP
SSC_URL=http://SERVER_IP/ssc/signin
AUTHNULL_LOGOUT_URL=http://SERVER_IP/ssc/signin
DOMAIN_URL identifies the tenant: the backend takes its second dot-separated label as the organization's database name, so it must be default.<ORG_NAME>.<domain>, and <ORG_NAME> must match the organization you create in Organization and Tenant Setup.
Use the customer's domain (for example customer-domain.com, the way AuthNull uses authnull.com), even in bare-IP mode - it is a name, not an address, so it needs no DNS record or certificate there. Users still browse to http://SERVER_IP.
TENANT_BASE_DOMAIN must be the domain part of DOMAIN_URL. When the organization is created, its tenant is stored as default.<ORG_NAME>.<TENANT_BASE_DOMAIN>, and sign-in finds the tenant by matching that against DOMAIN_URL - if the two differ, sign-in fails with "Tenant not found or inactive".
Passkeys (WebAuthn) need a domain, so they do not work in bare-IP mode. Push MFA and TOTP do.
Per-install secrets
Generate each one with openssl rand -hex 32 and paste it in:
JWT_SECRET=
INTERNAL_API_KEY=
Do not change ENCRYPTION_KEY or TOTP_ENCRYPTION_KEY after the stack has run. Changing them does not fail loudly - it makes existing encrypted data unreadable and locks every user out of TOTP MFA. Keep a backup of .env.
Sign-in factors
Set this so the sign-in page offers TOTP and passkeys directly. The mobile wallet is no longer used, and an empty value can send users with older console builds into a wallet-pairing screen:
WALLET_ONBOARDING_REQUIRED=false
Port conflicts
PAM_HOST_PORT defaults to 9090, which Cockpit uses on many Ubuntu installs. Change it if it collides.
Step 3: Install Docker and Docker Compose
Connect to the server over SSH, then install Docker and Docker Compose.
1. Update the package index
sudo apt-get update && sudo apt-get upgrade -y
2. Install prerequisite packages
sudo apt-get install -y ca-certificates curl
3. Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
4. Add the Docker repository to APT sources
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
5. Install Docker Engine, CLI, and Compose plugin
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
6. Start and enable the Docker service
sudo systemctl enable --now docker
sudo systemctl status docker
7. Allow your user to run Docker without sudo
sudo usermod -aG docker $USER
newgrp docker
Without this step, Docker commands fail with permission denied while trying to connect to the docker API; prefix them with sudo instead.
8. Verify the installation
docker --version
docker compose version
Step 4: Start the Stack
From the authnull-onprem-public directory:
docker compose pull
docker compose up -d
Step 5: Verify Container Status
docker compose ps -a
All services should show Up (and healthy where a health check is defined). The two one-off jobs, did-schema-init and cassandra-init, should show Exited (0). If did-schema-init exited with any other code, check it with docker compose logs did-schema-init.
Check the backend directly:
curl -s http://127.0.0.1:8080/system/v1/health
It should return {"status":"ok"}.
Updating the Stack
Images are published under fixed tags, and a new build is pushed under the same tag. docker compose up reuses an image that is already on the server, so always pull first:
docker compose pull <service>
docker compose up -d <service>
For example, docker compose pull did-react && docker compose up -d did-react updates the Admin UI.
authn-service must be tag 2.0.0 or later. Older tags have no push MFA: database logins are refused even when an MFA provider is configured.
Next Steps
Continue to Network Access: Domain or IP to publish the stack through Nginx.