Deploying Adriano ================= Adriano ships as one container image that serves the web UI and the API on port 8000. This section takes you from a published release to a running, healthy deployment, on Docker Compose or on Kubernetes. .. toctree:: :maxdepth: 1 docker-compose kubernetes What you deploy --------------- You run one image, ``ghcr.io/exhuma/adriano``, and connect it to three things you provide: PostgreSQL Version 18 is what Adriano is tested with. Adriano needs the database address as :envvar:`ADRIANO_DATABASE_URL`. You run and back up the database yourself (:doc:`/operator/backup-restore`). An OIDC identity provider People sign in there. See `Registering the client at your identity provider`_. A catalog file ``catalog.yaml`` lists the sources to index, the approval policy and the role permissions (:doc:`/operator/catalog-file`). You mount it read-only into the container. The container speaks plain HTTP. Put a TLS-terminating ingress or load balancer in front of it. .. mermaid:: flowchart LR browser["Browser"] ci["CI jobs"] cron["Poll schedule
(cron or CronJob)"] idp["OIDC provider
(yours)"] sources["Record sources
(GitLab, folders, ...)
(yours)"] db[("PostgreSQL
(yours)")] subgraph adriano["Adriano image"] app["web UI and API
port 8000"] end browser -- "HTTPS via your ingress" --> app browser -- "sign-in" --> idp ci -- "push candidates
(push token)" --> app app -- "verify tokens
(signing keys)" --> idp app -- "read records" --> sources cron -- "runs adriano poll" --> app app -- "reads and writes" --> db Prerequisites ------------- A container platform Docker Engine with Docker Compose v2.24 or newer, or a Kubernetes cluster. PostgreSQL A database and a user that may create tables. For production, use the two-user setup from :ref:`operator/backup-restore:Database roles and tamper resistance`. A TLS-terminating ingress or load balancer Point it at port 8000 of the container. An OIDC identity provider See the next section. At least one record source For example a GitLab instance and an access token, or a folder of decision records you can mount into the container (:doc:`/operator/sources`). A scheduler for polling cron or a systemd timer on the Docker host, or the Kubernetes ``CronJob`` from the example (:doc:`/operator/polling`). Registering the client at your identity provider ------------------------------------------------ Register one **public client** for the web UI: authorization code flow with PKCE, no client secret. Configure: Redirect URI ``https:///auth/callback`` Post-logout redirect URI ``https:///`` Web origin (CORS) ``https://``, if the provider restricts the browser origins that may call its token and key endpoints. Access token audience and roles The access token must carry the audience in :envvar:`ADRIANO_OIDC_AUDIENCE` and a list of role names at :envvar:`ADRIANO_OIDC_ROLES_CLAIM`. Then set :envvar:`ADRIANO_OIDC_AUTHORITY` and :envvar:`ADRIANO_OIDC_CLIENT_ID`. Both are required together. :doc:`/operator/authentication` describes the full contract. Image tags and channels ----------------------- Releases are published to the GitHub Container Registry as ``ghcr.io/exhuma/adriano``, tagged with a CalVer version ``YYYY.M.D`` such as ``2026.10.1`` (no leading zeros). Pre-releases carry a suffix, for example ``2026.10.1-beta.1``. The image is built for ``linux/amd64``. Besides the version tags, these moving tags exist: ``stable`` The newest final release. ``beta`` The newest final release or beta. ``alpha`` The newest final release, beta or alpha. ``rc`` The newest release candidate. ``edge`` The latest build of the main branch. Not a release: use it to try things out, never in production. ``sha-<7 characters>`` A build of one commit of the main branch. There is no ``latest`` tag. .. important:: In production, deploy one full version tag (for example ``2026.10.1``) and change it deliberately when you upgrade. A channel tag moves under you: a pod restart or a node change can silently run a newer release, whose database changes cannot be undone (:doc:`/operator/upgrades`). Quick check with ``docker run`` ------------------------------- To see the image start against a database and an identity provider, without Compose or Kubernetes: .. code-block:: sh docker run --rm -p 8000:8000 \ -e ADRIANO_DATABASE_URL=postgresql://adriano:@pg.example.com:5432/adriano \ -e ADRIANO_OIDC_AUTHORITY=https://idp.example.com/realms/adr \ -e ADRIANO_OIDC_CLIENT_ID=adriano-web \ -e ADRIANO_CONFIG_FILE=/etc/adriano/catalog.yaml \ -v "$PWD/catalog.yaml:/etc/adriano/catalog.yaml:ro" \ ghcr.io/exhuma/adriano: Then open http://localhost:8000 and check ``curl -fsS http://localhost:8000/healthz``. For anything lasting, use one of the two guides above. What happens at start --------------------- The container applies pending database migrations before it starts serving, unless you turn this off with :envvar:`ADRIANO_MIGRATE_ON_START`. Several containers starting at once are safe: only one applies the migrations while the others wait. A release may change the database schema in a way that cannot be undone by going back to the old version; back up before every upgrade (:doc:`/operator/upgrades`). The container refuses to start, and logs why, when: - :envvar:`ADRIANO_DATABASE_URL` is missing or not a ``postgresql://`` address; - :envvar:`ADRIANO_OIDC_AUTHORITY` is malformed, or only one of it and :envvar:`ADRIANO_OIDC_CLIENT_ID` is set. :doc:`/operator/troubleshooting` lists the exact messages. Local demo (optional) --------------------- To look at Adriano before you deploy it, a source checkout with the ``task`` command offers a throwaway demo with sample records and the development sign-in turned on: .. code-block:: sh task demo # build, start, poll and print the URLs task demo:down # stop and delete the demo stack and its data The demo is never a starting point for production: it enables the development sign-in on purpose. It runs under its own Compose project name, so ``task demo:down`` never touches a real deployment's data.