[Blog](https://deploywithox.com/blog) [Guides](https://deploywithox.com/blog#c-guides) # How to self-host authentik without Docker on your own VPS Run authentik 2026.8.4, the open-source SSO server, on your own Ubuntu server with ox: Postgres, a server and a worker, from the config that ran. Saurav Sharma Oct 17, 2026 10 min read On this page - [What you need](https://deploywithox.com/blog/self-host-authentik#what-you-need) - [The steps](https://deploywithox.com/blog/self-host-authentik#steps) - [The ox.toml, line by line](https://deploywithox.com/blog/self-host-authentik#config) - [Postgres and what to expect](https://deploywithox.com/blog/self-host-authentik#services) - [Updates and backups](https://deploywithox.com/blog/self-host-authentik#updates) - [Frequently asked questions](https://deploywithox.com/blog/self-host-authentik#faq) To self-host authentik you need an Ubuntu server, a fork of the authentik repo with one `ox.toml` file added, and 19 variables, of which you pick the values of only three. In our run the first deploy took about 24 minutes, which includes building the Python packages, the web interface and a Rust binary, and ox set up the Postgres database it needs. **How we tested** On Oct 17, 2026, ox deployed authentik 2026.8.4 from commit [`410f46e5`](https://github.com/goauthentik/authentik/commit/410f46e5cf18f97a1f0056b30984a145242df55d) on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory. The run then redeployed it, backed it up and rebooted the server. Then a separate test agent, one that did not write the config, signed in, created a user with a password, then signed in as that user, and checked the app's services and its backup. [authentik](https://github.com/goauthentik/authentik) is an identity provider. Your other apps send their sign-ins to it over OAuth2 and OpenID Connect, SAML, LDAP or RADIUS, and it keeps the users, groups and applications that decide who gets in. Most of the code is MIT licensed, and the `authentik/enterprise` folder has a license of its own. It has three parts: a server, a worker for background tasks such as email, and PostgreSQL. Here they are built on the server and run as system services. ox deployed authentik 2026.8.4 (commit 410f46e5) on Oct 17, 2026 on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory; the first deploy took 24 minutes and a redeploy took 13 minutes. - **First deploy:** 24 minutes, from adding the project until the app passed the run's first checks. - **Redeploy of the same commit:** 13 minutes, until the app passed the checks again. - **Server:** DigitalOcean, 8 vCPUs and 16 GB of memory (`s-8vcpu-16gb`), with this app alone on it. Every one of the 85 apps we deployed ran on this one size, so it was not picked for this app. - **Memory in use after the reboot:** 1460 MB of 15986 MB, for the whole server: Ubuntu, the app, its services, Caddy and the ox agent. On a server with no apps, ox's tests on real servers hold the agent to 40 MB or less and Caddy to 50 MB or less. - **Services ox runs for it:** `postgres`. - **Background processes:** `worker`, each its own systemd unit beside the web process. - **Source:** [github.com/goauthentik/authentik](https://github.com/goauthentik/authentik) at 2026.8.4, commit [`410f46e5`](https://github.com/goauthentik/authentik/commit/410f46e5cf18f97a1f0056b30984a145242df55d). The authentik login page The authentik Application Dashboard after signing in as the admin The authentik dashboard signed in as the new user verify-user, with its VU avatar ## What you need - An Ubuntu LTS server where you can run one command as root. The run used the size in the facts above, with authentik alone on it. ox works on any server from 1 GB up and tells you when memory is why an app stopped. - A GitHub account to fork `goauthentik/authentik`, and an ox account, which you open by signing in with GitHub. - A password and an address for the first admin. authentik sends mail through an SMTP server you name. The run pointed it at a test mail catcher, so email was not tested. ## The steps In the ox dashboard, press Add a server and run the install command it shows, as root on the server. It installs the ox agent and Caddy. Fork `goauthentik/authentik` and check out the release commit from the facts above. Add the `ox.toml` below at the repo's root, then commit and push. The run added no other file. Next write `authentik.env` on your laptop with the 19 keys listed under the config. You choose the secret key, the admin password and the admin email. authentik's Compose guide makes the secret key with `openssl rand -base64 60`, and the run used a random value. With the [ox CLI](https://deploywithox.com/docs/cli) signed in, run: Shell ```bash ox new /authentik ox review authentik --from-file authentik.env --wait ``` The project takes the repo's name. The review stores the values and begins the first deploy, and `--wait` streams the run's log until it ends. The [quickstart](https://deploywithox.com/docs/quickstart) shows the same two steps in the browser. When the deploy is green, open the address ox shows. Sign-in has two steps: type `akadmin` and press Log in, then give the password. The Admin interface button on the Application Dashboard opens the admin area. The test agent then made a user in the Admin interface, through Directory, Users, New User and Internal User, with a username and a display name. It opened the user and pressed Set password. The user was still listed after a reload, and signing in as that user worked. Connecting another app to authentik as a provider was not tested. The run served authentik on its preview address only. A domain of your own was not part of it, and the [domains page](https://deploywithox.com/docs/operate/domains) covers how ox adds one. ## The ox.toml, line by line TOML ```toml # ox deploy manifest for authentik (https://deploywithox.com). # Mirrors lifecycle/container/Dockerfile: the web UI (node-builder stage), the # Rust `authentik` binary (rust-builder stage), the Python venv (python-deps # stage), and compose.yml's two processes, `ak server` and `ak worker`. # AUTHENTIK_BOOTSTRAP_PASSWORD is the first admin's (user `akadmin`), applied # by blueprints/system/bootstrap.yaml at the first start: the only way in. required_variables = ["AUTHENTIK_SECRET_KEY", "AUTHENTIK_BOOTSTRAP_PASSWORD"] # The Dockerfile's apt lines, Debian names mapped to Ubuntu's: build headers # for the Python deps built from source (lxml and xmlsec link the system # libxml2, python-kadmin-rs links MIT and Heimdal kadm5, psycopg-c links # libpq), aws-lc-fips-sys for the Rust binary (cmake, clang), and the # runtime's libmaxminddb0 for GeoIP. packages = [ "pkg-config", "libffi-dev", "zlib1g-dev", "libxslt1-dev", "libpq-dev", "krb5-multidev", "libkrb5-dev", "heimdal-multidev", "libclang-dev", "libltdl-dev", "cmake", "clang", "libmaxminddb0", ] [tools] node = "26" # the Dockerfile's node:26 image (.nvmrc says 24.20; engines allow >=24) rust = "1.97.1" # rust-toolchain.toml [app] # lifecycle/ak is the image's entrypoint: it waits for postgres, makes the # Prometheus multiprocess dir gunicorn needs, and execs `authentik server` # (the binary the build puts on PATH). The HTTP listener takes $PORT; the # HTTPS and metrics listeners default to fixed ports (9443, 9300) that would # clash with the worker and with the second copy during a deploy, so they are # off. Each unit has its own /tmp, where the binary keeps its sockets. start = 'AUTHENTIK_LISTEN__HTTP="127.0.0.1:$PORT" AUTHENTIK_LISTEN__HTTPS="" AUTHENTIK_LISTEN__METRICS="" exec lifecycle/ak server' health = "/-/health/live/" [build] # The python-deps stage (the locked dependencies, without the project # itself) and node-builder's install: web/ is its own pnpm workspace with its # own lockfile, and the root workspace (lint tools) is not part of the image. install = "uv sync --locked --no-install-project --no-dev && pnpm --dir web install --frozen-lockfile" commands = [ # node-builder's build. "cd web && NODE_ENV=production pnpm run build && NODE_ENV=production pnpm run build:sfe", # rust-builder: the binary embeds web/dist/standalone/loading/startup.html, # so it builds after the UI. It goes into the venv's bin, where lifecycle/ak # finds it on PATH and the embedded Python finds the venv's pyvenv.cfg. # target/ (several GB) is a cache mount in the image, so it goes too. "AWS_LC_FIPS_SYS_CC=clang cargo build --package authentik --no-default-features --features core --locked --release && install -D target/release/authentik .venv/bin/authentik && rm -rf target", ] # The server and worker run lifecycle/migrate.py as they start; run it first # so a first deploy's 360+ migrations do not count against the health wait. migrate = "python -m lifecycle.migrate" [workers] # compose.yml's worker: background tasks, blueprints, outposts. It serves # /-/health/live/ on its own HTTP listener. worker = { run = 'AUTHENTIK_LISTEN__HTTP="127.0.0.1:$PORT" AUTHENTIK_LISTEN__HTTPS="" AUTHENTIK_LISTEN__METRICS="" exec lifecycle/ak worker', port = true, health = "/-/health/live/", stop_timeout = "10m" } [services] postgres = {} [storage] # compose.yml's volumes: ./data (media and files), ./certs, ./custom-templates keep = ["data", "certs", "custom-templates"] ``` The config repeats the stages of authentik's Dockerfile on the server: the web interface, a Rust binary and a Python environment. - `required_variables` names the two keys authentik cannot do without here: the secret key and the bootstrap password. ox asks for both before the first deploy. - `packages` holds the build libraries for the Python packages that compile from source, such as `lxml` and `xmlsec`, plus `cmake` and `clang` for the Rust binary and `libmaxminddb0` for GeoIP. - `[tools]` pins Node 26, as the Dockerfile's builder image does, and Rust 1.97.1 from `rust-toolchain.toml`. - `start` runs `lifecycle/ak server`, authentik's entrypoint script, which waits for Postgres and starts the `authentik` binary. HTTP listens on loopback at the `$PORT` ox gives each copy. The HTTPS and metrics listeners are off, because their fixed ports, 9443 and 9300, would clash with the worker and with the second copy ox runs during a deploy. - `health` is authentik's liveness path, and ox switches traffic only once it answers. - `install` runs `uv sync --locked --no-install-project --no-dev` for the Python packages, as `uv.lock` pins them, then installs the web interface's packages with pnpm. - The build `commands` build the web interface, then the Rust binary, which embeds a page from the web build. The binary goes into the Python environment's `bin` folder, where `lifecycle/ak` finds it, and the several-GB `target/` folder is deleted. - `migrate` runs authentik's migration handler before traffic switches. The server and worker also run it as they start, so the hundreds of migrations of a first deploy stay out of the health wait. - `[workers]` has authentik's worker. It serves HTTP on a port of its own, which ox checks before the app switches. Its `stop_timeout` of ten minutes matches how long authentik's worker waits for running tasks when stopped. The test agent saw tasks finish in its journal. - `[services]` asks for Postgres. `[storage]` keeps `data`, `certs` and `custom-templates` across deploys, so uploaded icons and custom email templates survive the next release. Shell ```bash AUTHENTIK_SECRET_KEY AUTHENTIK_BOOTSTRAP_PASSWORD AUTHENTIK_BOOTSTRAP_EMAIL AUTHENTIK_POSTGRESQL__HOST AUTHENTIK_POSTGRESQL__PORT AUTHENTIK_POSTGRESQL__USER AUTHENTIK_POSTGRESQL__PASSWORD AUTHENTIK_POSTGRESQL__NAME AUTHENTIK_WEB__BASE_URL AUTHENTIK_BLUEPRINTS_DIR AUTHENTIK_CERT_DISCOVERY_DIR AUTHENTIK_EMAIL__TEMPLATE_DIR KRB5_CONFIG AUTHENTIK_EMAIL__HOST AUTHENTIK_EMAIL__PORT AUTHENTIK_EMAIL__USE_TLS AUTHENTIK_EMAIL__FROM PYTHONDONTWRITEBYTECODE RUST_BACKTRACE ``` Three keys are yours. `AUTHENTIK_SECRET_KEY` signs cookies, and changing it later ends active sessions. `AUTHENTIK_BOOTSTRAP_PASSWORD` and `AUTHENTIK_BOOTSTRAP_EMAIL` belong to the first admin, `akadmin`, which a bootstrap blueprint creates at the first start only. authentik's [automated install page](https://docs.goauthentik.io/docs/install-config/automated-install) prefers the hashed form, `AUTHENTIK_BOOTSTRAP_PASSWORD_HASH`. The run used the plain password, and the hash was not tested. Each `AUTHENTIK_POSTGRESQL__*` key is a reference to a field of ox's Postgres service, such as `${postgres.host}`. `AUTHENTIK_WEB__BASE_URL` is `${PUBLIC_URL}`, because authentik 2026.8 warns in the Admin interface until its base URL is set. See [variables](https://deploywithox.com/docs/operate/variables) for how references work. `AUTHENTIK_BLUEPRINTS_DIR`, `AUTHENTIK_CERT_DISCOVERY_DIR` and `AUTHENTIK_EMAIL__TEMPLATE_DIR` point at folders inside the release, where the image keeps `/blueprints`, `/certs` and `/templates`. `KRB5_CONFIG` points at the repo's Kerberos file, which the image copies to `/etc/krb5.conf`. `PYTHONDONTWRITEBYTECODE` and `RUST_BACKTRACE` copy the image's own environment lines. The other four `AUTHENTIK_EMAIL__*` keys are the mail host, port, STARTTLS switch and sender. The run pointed host and port at a test mail catcher, so no real mail went out. ## Postgres and what to expect authentik needs PostgreSQL and no other service. ox gives the project its own database and role in the server's shared Postgres. Look inside from the Services tab or with `ox explore authentik postgres`. For up to about 30 seconds after a deploy or restart, the site shows "Server is starting up. Refreshing in a few seconds..." while its Python workers boot, then reloads by itself. The journal warns that the GeoIP database did not load, because authentik's image build fetches it with a MaxMind account and a build from the repo has none. There is no Forgot password link, since no recovery flow is set up by default. An admin resets passwords under Directory, then Users. ## Updates and backups To update, merge a new release commit into your fork and push, or run `ox deploy authentik --wait`. ox starts the new release beside the running one and switches when its health path answers. The run redeployed the same commit, and a move to a newer release was not tested. authentik's [upgrade page](https://docs.goauthentik.io/docs/install-config/upgrade) says it does not support downgrading. Backups need no setup. The run's were Postgres dumps: one from Back up now, one before the redeploy's migrate, and the daily one. `ox backups authentik` lists them. A restore was not tested. The [backups page](https://deploywithox.com/docs/operate/backups) covers copies in an S3 bucket. ## Frequently asked questions ### Do I need Docker to self-host authentik? No. authentik's docs lead with Docker Compose and Helm, but the code is a Python app with a Rust server and a web build. ox builds it from your fork on the server and runs the server and the worker as system services beside Postgres. An update is a push or one deploy command. ### What is the first admin login for authentik? The user is akadmin, with the password from AUTHENTIK_BOOTSTRAP_PASSWORD. A bootstrap blueprint creates it at the first start, and because the password is set, the browser's initial setup step is skipped. There is no sign-up page. Changing the variable later does not change the password, so reset it under Directory, then Users. ### How much memory does self-hosted authentik use? On the run's server, the whole machine used the memory the facts show after a reboot, counting authentik's server and worker, Postgres, Caddy and the ox agent. The build also compiles a Rust binary there. ox takes any server from 1 GB up and says so when memory is why an app stopped. ### Which database does authentik need? PostgreSQL, and nothing else. authentik's architecture page lists three parts, the server, the worker and PostgreSQL, which holds users, flows, policies and settings. With ox, one line, `postgres = {}` under `[services]`, gives the project a database and a role, and five variables pass the details on. New to running apps on your own server? [How ox self-hosts your apps](https://deploywithox.com/self-hosting) shows what ox sets up on your server. On this page - [What you need](https://deploywithox.com/blog/self-host-authentik#what-you-need) - [The steps](https://deploywithox.com/blog/self-host-authentik#steps) - [The ox.toml, line by line](https://deploywithox.com/blog/self-host-authentik#config) - [Postgres and what to expect](https://deploywithox.com/blog/self-host-authentik#services) - [Updates and backups](https://deploywithox.com/blog/self-host-authentik#updates) - [Frequently asked questions](https://deploywithox.com/blog/self-host-authentik#faq) - [Do I need Docker to self-host authentik?](https://deploywithox.com/blog/self-host-authentik#faq-docker) - [What is the first admin login for authentik?](https://deploywithox.com/blog/self-host-authentik#faq-admin) - [How much memory does self-hosted authentik use?](https://deploywithox.com/blog/self-host-authentik#faq-memory) - [Which database does authentik need?](https://deploywithox.com/blog/self-host-authentik#faq-database) Written by Saurav Sharma Builds ox. Writes about running apps on servers you own, without Docker and without a platform bill. [Older post How to self-host Bagisto without Docker on your own VPS](https://deploywithox.com/blog/self-host-bagisto) [Newer post How to self-host Apache Answer on your own Ubuntu VPS](https://deploywithox.com/blog/self-host-answer) ## Keep reading - Guides [Self-Hosted Open-Source Apps Deployed with ox: All 85](https://deploywithox.com/blog/self-hosted-open-source-apps-deployed-with-ox) ox deployed 85 open-source apps from pinned commits on fresh servers, with no Docker. The stacks, services, deploy times and a table of every app. Saurav Sharma Oct 17, 2026 16 min read - Guides [How to self-host AFFiNE on your own VPS without Docker](https://deploywithox.com/blog/self-host-affine) Run AFFiNE v0.27.4 on your own Ubuntu server with ox: Postgres with pgvector, Redis, a Rust and Node build, and an admin made from two variables. Saurav Sharma Oct 17, 2026 12 min read - Guides [How to self-host Akaunting on your own VPS without Docker](https://deploywithox.com/blog/self-host-akaunting) Run Akaunting 3.2.4 on your own Ubuntu server with ox: PHP, MariaDB, a scheduler and the first company and admin made on deploy, from the config that ran. Saurav Sharma Oct 17, 2026 10 min read ## Deploy to a server you own ox sets up systemd, Caddy and PostgreSQL on your Ubuntu server and deploys on every push. Free for your first project. [Sign up with GitHub](https://deploywithox.com/login/github) [Read the docs](https://deploywithox.com/docs)