feat(oci): add Authentik catalog profile

This commit is contained in:
Jonatan
2026-10-30 19:24:01 +01:00
parent 89531bbab8
commit 3b15e14625
5 changed files with 1469 additions and 3 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. --> <!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
44 of 530 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists. 44 of 531 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it. Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
+645
View File
@@ -0,0 +1,645 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-authentik",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Authentik"
},
"tagline": {
"en_US": "Identity provider for single sign-on and access control"
},
"description": {
"en_US": "Authentik is an open-source identity provider and access platform. This recipe follows the official three-service deployment with PostgreSQL, the server and a worker, each in its own Proxmox OCI LXC."
},
"category": "security",
"category_label": "Authentication & Security",
"author": "Authentik",
"developer": "Authentik",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/authentik.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 9000,
"path": "/"
},
"website": "https://goauthentik.io/",
"documentation": "https://docs.goauthentik.io/install-config/install/docker-compose/",
"repository": "https://github.com/goauthentik/authentik",
"tips": [
"The server and worker share /data and /templates through host directories; those directories are not included in Proxmox CT backups.",
"The worker Docker-socket mount is omitted. Use the embedded outpost or deploy and manage a separate outpost manually."
],
"mini_changelog": [],
"display_version": "2026.8.5",
"updated_at": "2026-10-21"
},
"source": {
"provider": "goauthentik",
"repository": "https://github.com/goauthentik/authentik",
"default_branch": "main",
"revision": "4ec0d19b98da3c329781894cd550da95d3033954",
"readme_raw_url": "https://goauthentik.io/version/2026.8/lifecycle/container/compose.yml",
"image_repository_url": "https://github.com/goauthentik/authentik/pkgs/container/server",
"readme_pushed_at": "2026-10-21T12:57:59Z",
"compose_sha256": "a3e138b5a85844d15c4a7ba6339997e43c0479d9d198582c67dd19523aadc694",
"generated_at": "2026-10-30T18:09:01+00:00"
},
"container_contract": {
"service_name": "server",
"container_name": "authentik-server",
"image": {
"reference": "ghcr.io/goauthentik/server:2026.8.5",
"registry": "ghcr.io",
"repository": "ghcr.io/goauthentik/server",
"tag": "2026.8.5",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "AUTHENTIK_POSTGRESQL__HOST",
"example": "postgresql",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__NAME",
"example": "authentik",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__PASSWORD",
"example": "${GENERATED_GENERATED_PG_PASS}",
"required": true,
"sensitive": true,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__USER",
"example": "authentik",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_SECRET_KEY",
"example": "${GENERATED_GENERATED_AUTHENTIK_SECRET_KEY}",
"required": true,
"sensitive": true,
"source": "docker-compose"
}
],
"volumes": [
{
"id": "volume-0",
"container_path": "/data",
"compose_source_example": "./data",
"read_only": false,
"required": true,
"installation_choice": [
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": false,
"default_size_gb": 8
}
},
{
"id": "volume-1",
"container_path": "/templates",
"compose_source_example": "./custom-templates",
"read_only": false,
"required": true,
"installation_choice": [
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": false,
"default_size_gb": 8
}
}
],
"ports": [
{
"container_port": 9000,
"published_example": 9000,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 9443,
"published_example": 9443,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [
{
"name": "postgresql",
"image": "docker.io/library/postgres:16-alpine"
},
{
"name": "worker",
"image": "ghcr.io/goauthentik/server:2026.8.5"
}
],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: authentik\nservices:\n postgresql:\n environment:\n POSTGRES_DB: authentik\n POSTGRES_PASSWORD: ${GENERATED_PG_PASS}\n POSTGRES_USER: authentik\n healthcheck:\n interval: 30s\n retries: 5\n start_period: 20s\n test:\n - CMD-SHELL\n - pg_isready -h 127.0.0.1 -d authentik -U authentik\n timeout: 5s\n image: docker.io/library/postgres:16-alpine\n restart: unless-stopped\n volumes:\n - read_only: false\n source: database\n target: /var/lib/postgresql/data\n server:\n command: server\n depends_on:\n postgresql:\n condition: service_healthy\n environment:\n AUTHENTIK_POSTGRESQL__HOST: postgresql\n AUTHENTIK_POSTGRESQL__NAME: authentik\n AUTHENTIK_POSTGRESQL__PASSWORD: ${GENERATED_PG_PASS}\n AUTHENTIK_POSTGRESQL__USER: authentik\n AUTHENTIK_SECRET_KEY: ${GENERATED_AUTHENTIK_SECRET_KEY}\n image: ghcr.io/goauthentik/server:2026.8.5\n ports:\n - 9000:9000\n - 9443:9443\n restart: unless-stopped\n shm_size: 512mb\n volumes:\n - read_only: false\n source: ./data\n target: /data\n - read_only: false\n source: ./custom-templates\n target: /templates\n worker:\n command: worker\n depends_on:\n postgresql:\n condition: service_healthy\n environment:\n AUTHENTIK_POSTGRESQL__HOST: postgresql\n AUTHENTIK_POSTGRESQL__NAME: authentik\n AUTHENTIK_POSTGRESQL__PASSWORD: ${GENERATED_PG_PASS}\n AUTHENTIK_POSTGRESQL__USER: authentik\n AUTHENTIK_SECRET_KEY: ${GENERATED_AUTHENTIK_SECRET_KEY}\n image: ghcr.io/goauthentik/server:2026.8.5\n restart: unless-stopped\n shm_size: 512mb\n user: root\n volumes:\n - read_only: false\n source: ./data\n target: /data\n - read_only: false\n source: ./certs\n target: /certs\n - read_only: false\n source: ./custom-templates\n target: /templates\nvolumes:\n database: {}\n"
},
"compose_stack": {
"project_name": "authentik",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "server",
"service_count": 3,
"services": [
{
"name": "postgresql",
"image": "docker.io/library/postgres:16-alpine",
"is_main": false,
"role": "dependency",
"vmid_offset": 1,
"depends_on": [],
"frontend_network": false,
"private_network": true,
"compose": {
"environment": {
"POSTGRES_DB": "authentik",
"POSTGRES_PASSWORD": "${GENERATED_PG_PASS}",
"POSTGRES_USER": "authentik"
},
"healthcheck": {
"interval": "30s",
"retries": 5,
"start_period": "20s",
"test": [
"CMD-SHELL",
"pg_isready -h 127.0.0.1 -d authentik -U authentik"
],
"timeout": "5s"
},
"image": "docker.io/library/postgres:16-alpine",
"restart": "unless-stopped",
"volumes": [
{
"source": "database",
"target": "/var/lib/postgresql/data",
"read_only": false
}
]
}
},
{
"name": "worker",
"image": "ghcr.io/goauthentik/server:2026.8.5",
"is_main": false,
"role": "dependency",
"vmid_offset": 2,
"depends_on": [
"postgresql"
],
"frontend_network": false,
"private_network": true,
"compose": {
"command": "worker",
"depends_on": {
"postgresql": {
"condition": "service_healthy"
}
},
"environment": {
"AUTHENTIK_POSTGRESQL__HOST": "postgresql",
"AUTHENTIK_POSTGRESQL__NAME": "authentik",
"AUTHENTIK_POSTGRESQL__PASSWORD": "${GENERATED_PG_PASS}",
"AUTHENTIK_POSTGRESQL__USER": "authentik",
"AUTHENTIK_SECRET_KEY": "${GENERATED_AUTHENTIK_SECRET_KEY}"
},
"image": "ghcr.io/goauthentik/server:2026.8.5",
"restart": "unless-stopped",
"shm_size": "512mb",
"user": "root",
"volumes": [
{
"source": "./data",
"target": "/data",
"read_only": false
},
{
"source": "./certs",
"target": "/certs",
"read_only": false
},
{
"source": "./custom-templates",
"target": "/templates",
"read_only": false
}
]
}
},
{
"name": "server",
"image": "ghcr.io/goauthentik/server:2026.8.5",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [
"postgresql"
],
"frontend_network": true,
"private_network": true,
"compose": {
"command": "server",
"depends_on": {
"postgresql": {
"condition": "service_healthy"
}
},
"environment": {
"AUTHENTIK_POSTGRESQL__HOST": "postgresql",
"AUTHENTIK_POSTGRESQL__NAME": "authentik",
"AUTHENTIK_POSTGRESQL__PASSWORD": "${GENERATED_PG_PASS}",
"AUTHENTIK_POSTGRESQL__USER": "authentik",
"AUTHENTIK_SECRET_KEY": "${GENERATED_AUTHENTIK_SECRET_KEY}"
},
"image": "ghcr.io/goauthentik/server:2026.8.5",
"ports": [
"9000:9000",
"9443:9443"
],
"restart": "unless-stopped",
"shm_size": "512mb",
"volumes": [
{
"source": "./data",
"target": "/data",
"read_only": false
},
{
"source": "./custom-templates",
"target": "/templates",
"read_only": false
}
]
}
}
],
"top_level": {
"name": "authentik",
"volumes": {
"database": {}
}
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": true,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "postgresql-volume-0",
"service": "postgresql",
"container_path": "/var/lib/postgresql/data",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "server-volume-0",
"service": "server",
"container_path": "/data",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/data"
},
{
"id": "server-volume-1",
"service": "server",
"container_path": "/templates",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/templates"
},
{
"id": "worker-volume-0",
"service": "worker",
"container_path": "/data",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/data"
},
{
"id": "worker-volume-1",
"service": "worker",
"container_path": "/certs",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "worker-volume-2",
"service": "worker",
"container_path": "/templates",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/templates"
}
],
"orchestration": {
"reserve_vmids_atomically": 3,
"start_order": [
"postgresql",
"worker",
"server"
],
"stop_order": [
"server",
"worker",
"postgresql"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "pg-pass",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "postgresql",
"environment_variable": "POSTGRES_PASSWORD"
},
{
"service": "server",
"environment_variable": "AUTHENTIK_POSTGRESQL__PASSWORD"
},
{
"service": "worker",
"environment_variable": "AUTHENTIK_POSTGRESQL__PASSWORD"
}
]
},
{
"id": "authentik-secret-key",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "server",
"environment_variable": "AUTHENTIK_SECRET_KEY"
},
{
"service": "worker",
"environment_variable": "AUTHENTIK_SECRET_KEY"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "Web interface",
"scheme": "http",
"port": 9000,
"path": "/",
"source": "official-compose"
}
],
"credentials": [
{
"label": "Initial administrator account",
"type": "static-default",
"username": "akadmin",
"password": null,
"change_required": true,
"source": "Authentik documentation; set the akadmin password during the first-run setup"
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"installer_profile": {
"stack_driver": "generic-multi-lxc-stack",
"volume_preparations": [
{
"container_path": "/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/templates",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/certs",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/var/lib/postgresql/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
]
},
"adaptations": [
{
"id": "worker-docker-socket-removed",
"upstream_behavior": "The official Compose mounts the Docker socket into the worker for automatic deployment and management of separate outposts.",
"native_lxc_behavior": "The Docker socket mount is omitted. Authentik itself runs as server, PostgreSQL and worker in native OCI LXCs.",
"reason": "The ProxMenux native OCI stack does not run Docker Engine or provide a Docker daemon socket.",
"behavioral_impact": "Use the embedded outpost for a simple proxy-provider deployment, or deploy and manage an outpost separately.",
"validation": "passed-ms03-2026-10-30"
},
{
"id": "postgres-healthcheck-normalized",
"upstream_behavior": "The official PostgreSQL healthcheck expands Compose environment variables in its command.",
"native_lxc_behavior": "The check uses the recipe’s fixed database and username with pg_isready against localhost.",
"reason": "The native stack healthcheck runs in the database LXC and does not evaluate Compose variable interpolation.",
"behavioral_impact": "It checks the same local database readiness before dependent services start.",
"validation": "passed-ms03-postgresql-readiness-2026-10-30"
},
{
"id": "server-worker-shared-data",
"upstream_behavior": "Authentik server and worker both mount /data and /templates from the same Compose source directories.",
"native_lxc_behavior": "ProxMenux uses shared host directories for these two paths across the server and worker LXCs.",
"reason": "The same data and custom templates must be visible to both services; the stack driver shares named Compose sources through host paths.",
"behavioral_impact": "These shared host directories are not included in native LXC backups and need a separate backup.",
"validation": "passed-ms03-2026-10-30"
}
],
"custom_compose_stack": true,
"custom_compose_interpolated": false,
"stack_image_entrypoints": {}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Official Authentik Compose translated to three native OCI LXCs. The Docker socket mount is deliberately removed; the embedded outpost remains available."
},
"validation": {
"schema": "passed-local-2026-10-30",
"image_pull": "passed-ms03-skopeo-and-OCI-integrity-server-worker-postgresql-2026-10-30",
"default_install": "passed-ms03-three-LXCs-started-PostgreSQL-healthy-2026-10-30",
"advanced_install": "pending",
"credentials": "pending-user-first-run-and-login",
"web_ui": "pending-user-first-run-and-login",
"storage_modes": "pending",
"restart_persistence": "pending",
"backup_restore": "pending",
"update_preserves_data": "pending",
"rollback": "pending",
"modification": "pending",
"removal": "pending",
"oci_engine_tests": "pending",
"offline_i18n_tests": "pending",
"runtime_architecture": "amd64-ms03; Skopeo manifests include amd64-and-arm64",
"database_health": "passed-pg_isready-2026-10-30"
},
"lifecycle": {
"update_strategy": "resolve-selected-tag-and-architecture-digest-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": "2026.8.5",
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-image-digest",
"automatic_unattended_updates": false,
"dependency_lifecycle": {
"implementation": "proxmox-hookscript",
"trigger": "main-lxc-pre-start",
"waits_for_dependency_healthchecks": true,
"stops_dependencies_with_main": false
}
}
}
+645
View File
@@ -0,0 +1,645 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-authentik",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Authentik"
},
"tagline": {
"en_US": "Identity provider for single sign-on and access control"
},
"description": {
"en_US": "Authentik is an open-source identity provider and access platform. This recipe follows the official three-service deployment with PostgreSQL, the server and a worker, each in its own Proxmox OCI LXC."
},
"category": "security",
"category_label": "Authentication & Security",
"author": "Authentik",
"developer": "Authentik",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/authentik.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 9000,
"path": "/"
},
"website": "https://goauthentik.io/",
"documentation": "https://docs.goauthentik.io/install-config/install/docker-compose/",
"repository": "https://github.com/goauthentik/authentik",
"tips": [
"The server and worker share /data and /templates through host directories; those directories are not included in Proxmox CT backups.",
"The worker Docker-socket mount is omitted. Use the embedded outpost or deploy and manage a separate outpost manually."
],
"mini_changelog": [],
"display_version": "2026.8.5",
"updated_at": "2026-10-21"
},
"source": {
"provider": "goauthentik",
"repository": "https://github.com/goauthentik/authentik",
"default_branch": "main",
"revision": "4ec0d19b98da3c329781894cd550da95d3033954",
"readme_raw_url": "https://goauthentik.io/version/2026.8/lifecycle/container/compose.yml",
"image_repository_url": "https://github.com/goauthentik/authentik/pkgs/container/server",
"readme_pushed_at": "2026-10-21T12:57:59Z",
"compose_sha256": "a3e138b5a85844d15c4a7ba6339997e43c0479d9d198582c67dd19523aadc694",
"generated_at": "2026-10-30T18:09:01+00:00"
},
"container_contract": {
"service_name": "server",
"container_name": "authentik-server",
"image": {
"reference": "ghcr.io/goauthentik/server:2026.8.5",
"registry": "ghcr.io",
"repository": "ghcr.io/goauthentik/server",
"tag": "2026.8.5",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "AUTHENTIK_POSTGRESQL__HOST",
"example": "postgresql",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__NAME",
"example": "authentik",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__PASSWORD",
"example": "${GENERATED_GENERATED_PG_PASS}",
"required": true,
"sensitive": true,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_POSTGRESQL__USER",
"example": "authentik",
"required": true,
"sensitive": false,
"source": "docker-compose"
},
{
"name": "AUTHENTIK_SECRET_KEY",
"example": "${GENERATED_GENERATED_AUTHENTIK_SECRET_KEY}",
"required": true,
"sensitive": true,
"source": "docker-compose"
}
],
"volumes": [
{
"id": "volume-0",
"container_path": "/data",
"compose_source_example": "./data",
"read_only": false,
"required": true,
"installation_choice": [
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": false,
"default_size_gb": 8
}
},
{
"id": "volume-1",
"container_path": "/templates",
"compose_source_example": "./custom-templates",
"read_only": false,
"required": true,
"installation_choice": [
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": false,
"default_size_gb": 8
}
}
],
"ports": [
{
"container_port": 9000,
"published_example": 9000,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 9443,
"published_example": 9443,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [
{
"name": "postgresql",
"image": "docker.io/library/postgres:16-alpine"
},
{
"name": "worker",
"image": "ghcr.io/goauthentik/server:2026.8.5"
}
],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: authentik\nservices:\n postgresql:\n environment:\n POSTGRES_DB: authentik\n POSTGRES_PASSWORD: ${GENERATED_PG_PASS}\n POSTGRES_USER: authentik\n healthcheck:\n interval: 30s\n retries: 5\n start_period: 20s\n test:\n - CMD-SHELL\n - pg_isready -h 127.0.0.1 -d authentik -U authentik\n timeout: 5s\n image: docker.io/library/postgres:16-alpine\n restart: unless-stopped\n volumes:\n - read_only: false\n source: database\n target: /var/lib/postgresql/data\n server:\n command: server\n depends_on:\n postgresql:\n condition: service_healthy\n environment:\n AUTHENTIK_POSTGRESQL__HOST: postgresql\n AUTHENTIK_POSTGRESQL__NAME: authentik\n AUTHENTIK_POSTGRESQL__PASSWORD: ${GENERATED_PG_PASS}\n AUTHENTIK_POSTGRESQL__USER: authentik\n AUTHENTIK_SECRET_KEY: ${GENERATED_AUTHENTIK_SECRET_KEY}\n image: ghcr.io/goauthentik/server:2026.8.5\n ports:\n - 9000:9000\n - 9443:9443\n restart: unless-stopped\n shm_size: 512mb\n volumes:\n - read_only: false\n source: ./data\n target: /data\n - read_only: false\n source: ./custom-templates\n target: /templates\n worker:\n command: worker\n depends_on:\n postgresql:\n condition: service_healthy\n environment:\n AUTHENTIK_POSTGRESQL__HOST: postgresql\n AUTHENTIK_POSTGRESQL__NAME: authentik\n AUTHENTIK_POSTGRESQL__PASSWORD: ${GENERATED_PG_PASS}\n AUTHENTIK_POSTGRESQL__USER: authentik\n AUTHENTIK_SECRET_KEY: ${GENERATED_AUTHENTIK_SECRET_KEY}\n image: ghcr.io/goauthentik/server:2026.8.5\n restart: unless-stopped\n shm_size: 512mb\n user: root\n volumes:\n - read_only: false\n source: ./data\n target: /data\n - read_only: false\n source: ./certs\n target: /certs\n - read_only: false\n source: ./custom-templates\n target: /templates\nvolumes:\n database: {}\n"
},
"compose_stack": {
"project_name": "authentik",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "server",
"service_count": 3,
"services": [
{
"name": "postgresql",
"image": "docker.io/library/postgres:16-alpine",
"is_main": false,
"role": "dependency",
"vmid_offset": 1,
"depends_on": [],
"frontend_network": false,
"private_network": true,
"compose": {
"environment": {
"POSTGRES_DB": "authentik",
"POSTGRES_PASSWORD": "${GENERATED_PG_PASS}",
"POSTGRES_USER": "authentik"
},
"healthcheck": {
"interval": "30s",
"retries": 5,
"start_period": "20s",
"test": [
"CMD-SHELL",
"pg_isready -h 127.0.0.1 -d authentik -U authentik"
],
"timeout": "5s"
},
"image": "docker.io/library/postgres:16-alpine",
"restart": "unless-stopped",
"volumes": [
{
"source": "database",
"target": "/var/lib/postgresql/data",
"read_only": false
}
]
}
},
{
"name": "worker",
"image": "ghcr.io/goauthentik/server:2026.8.5",
"is_main": false,
"role": "dependency",
"vmid_offset": 2,
"depends_on": [
"postgresql"
],
"frontend_network": false,
"private_network": true,
"compose": {
"command": "worker",
"depends_on": {
"postgresql": {
"condition": "service_healthy"
}
},
"environment": {
"AUTHENTIK_POSTGRESQL__HOST": "postgresql",
"AUTHENTIK_POSTGRESQL__NAME": "authentik",
"AUTHENTIK_POSTGRESQL__PASSWORD": "${GENERATED_PG_PASS}",
"AUTHENTIK_POSTGRESQL__USER": "authentik",
"AUTHENTIK_SECRET_KEY": "${GENERATED_AUTHENTIK_SECRET_KEY}"
},
"image": "ghcr.io/goauthentik/server:2026.8.5",
"restart": "unless-stopped",
"shm_size": "512mb",
"user": "root",
"volumes": [
{
"source": "./data",
"target": "/data",
"read_only": false
},
{
"source": "./certs",
"target": "/certs",
"read_only": false
},
{
"source": "./custom-templates",
"target": "/templates",
"read_only": false
}
]
}
},
{
"name": "server",
"image": "ghcr.io/goauthentik/server:2026.8.5",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [
"postgresql"
],
"frontend_network": true,
"private_network": true,
"compose": {
"command": "server",
"depends_on": {
"postgresql": {
"condition": "service_healthy"
}
},
"environment": {
"AUTHENTIK_POSTGRESQL__HOST": "postgresql",
"AUTHENTIK_POSTGRESQL__NAME": "authentik",
"AUTHENTIK_POSTGRESQL__PASSWORD": "${GENERATED_PG_PASS}",
"AUTHENTIK_POSTGRESQL__USER": "authentik",
"AUTHENTIK_SECRET_KEY": "${GENERATED_AUTHENTIK_SECRET_KEY}"
},
"image": "ghcr.io/goauthentik/server:2026.8.5",
"ports": [
"9000:9000",
"9443:9443"
],
"restart": "unless-stopped",
"shm_size": "512mb",
"volumes": [
{
"source": "./data",
"target": "/data",
"read_only": false
},
{
"source": "./custom-templates",
"target": "/templates",
"read_only": false
}
]
}
}
],
"top_level": {
"name": "authentik",
"volumes": {
"database": {}
}
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": true,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "postgresql-volume-0",
"service": "postgresql",
"container_path": "/var/lib/postgresql/data",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "server-volume-0",
"service": "server",
"container_path": "/data",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/data"
},
{
"id": "server-volume-1",
"service": "server",
"container_path": "/templates",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/templates"
},
{
"id": "worker-volume-0",
"service": "worker",
"container_path": "/data",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/data"
},
{
"id": "worker-volume-1",
"service": "worker",
"container_path": "/certs",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "worker-volume-2",
"service": "worker",
"container_path": "/templates",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for authentik:/templates"
}
],
"orchestration": {
"reserve_vmids_atomically": 3,
"start_order": [
"postgresql",
"worker",
"server"
],
"stop_order": [
"server",
"worker",
"postgresql"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "pg-pass",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "postgresql",
"environment_variable": "POSTGRES_PASSWORD"
},
{
"service": "server",
"environment_variable": "AUTHENTIK_POSTGRESQL__PASSWORD"
},
{
"service": "worker",
"environment_variable": "AUTHENTIK_POSTGRESQL__PASSWORD"
}
]
},
{
"id": "authentik-secret-key",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "server",
"environment_variable": "AUTHENTIK_SECRET_KEY"
},
{
"service": "worker",
"environment_variable": "AUTHENTIK_SECRET_KEY"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "Web interface",
"scheme": "http",
"port": 9000,
"path": "/",
"source": "official-compose"
}
],
"credentials": [
{
"label": "Initial administrator account",
"type": "static-default",
"username": "akadmin",
"password": null,
"change_required": true,
"source": "Authentik documentation; set the akadmin password during the first-run setup"
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"installer_profile": {
"stack_driver": "generic-multi-lxc-stack",
"volume_preparations": [
{
"container_path": "/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/templates",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/certs",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/var/lib/postgresql/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
]
},
"adaptations": [
{
"id": "worker-docker-socket-removed",
"upstream_behavior": "The official Compose mounts the Docker socket into the worker for automatic deployment and management of separate outposts.",
"native_lxc_behavior": "The Docker socket mount is omitted. Authentik itself runs as server, PostgreSQL and worker in native OCI LXCs.",
"reason": "The ProxMenux native OCI stack does not run Docker Engine or provide a Docker daemon socket.",
"behavioral_impact": "Use the embedded outpost for a simple proxy-provider deployment, or deploy and manage an outpost separately.",
"validation": "passed-ms03-2026-10-30"
},
{
"id": "postgres-healthcheck-normalized",
"upstream_behavior": "The official PostgreSQL healthcheck expands Compose environment variables in its command.",
"native_lxc_behavior": "The check uses the recipe’s fixed database and username with pg_isready against localhost.",
"reason": "The native stack healthcheck runs in the database LXC and does not evaluate Compose variable interpolation.",
"behavioral_impact": "It checks the same local database readiness before dependent services start.",
"validation": "passed-ms03-postgresql-readiness-2026-10-30"
},
{
"id": "server-worker-shared-data",
"upstream_behavior": "Authentik server and worker both mount /data and /templates from the same Compose source directories.",
"native_lxc_behavior": "ProxMenux uses shared host directories for these two paths across the server and worker LXCs.",
"reason": "The same data and custom templates must be visible to both services; the stack driver shares named Compose sources through host paths.",
"behavioral_impact": "These shared host directories are not included in native LXC backups and need a separate backup.",
"validation": "passed-ms03-2026-10-30"
}
],
"custom_compose_stack": true,
"custom_compose_interpolated": false,
"stack_image_entrypoints": {}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Official Authentik Compose translated to three native OCI LXCs. The Docker socket mount is deliberately removed; the embedded outpost remains available."
},
"validation": {
"schema": "passed-local-2026-10-30",
"image_pull": "passed-ms03-skopeo-and-OCI-integrity-server-worker-postgresql-2026-10-30",
"default_install": "passed-ms03-three-LXCs-started-PostgreSQL-healthy-2026-10-30",
"advanced_install": "pending",
"credentials": "pending-user-first-run-and-login",
"web_ui": "pending-user-first-run-and-login",
"storage_modes": "pending",
"restart_persistence": "pending",
"backup_restore": "pending",
"update_preserves_data": "pending",
"rollback": "pending",
"modification": "pending",
"removal": "pending",
"oci_engine_tests": "pending",
"offline_i18n_tests": "pending",
"runtime_architecture": "amd64-ms03; Skopeo manifests include amd64-and-arm64",
"database_health": "passed-pg_isready-2026-10-30"
},
"lifecycle": {
"update_strategy": "resolve-selected-tag-and-architecture-digest-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": "2026.8.5",
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-image-digest",
"automatic_unattended_updates": false,
"dependency_lifecycle": {
"implementation": "proxmox-hookscript",
"trigger": "main-lxc-pre-start",
"waits_for_dependency_healthchecks": true,
"stops_dependencies_with_main": false
}
}
}
+35 -2
View File
@@ -2,7 +2,7 @@
"schema_version": "0.2.0", "schema_version": "0.2.0",
"kind": "proxmenux.oci-catalog-index", "kind": "proxmenux.oci-catalog-index",
"provider": "multiple-container-images", "provider": "multiple-container-images",
"generated_at": "2026-10-26T08:05:54+00:00", "generated_at": "2026-10-30T18:13:36+00:00",
"applications": [ "applications": [
{ {
"id": "2fauth", "id": "2fauth",
@@ -752,6 +752,39 @@
"hidden": false, "hidden": false,
"template_id": "image-audiobookshelf" "template_id": "image-audiobookshelf"
}, },
{
"id": "authentik",
"provider": "goauthentik",
"template_family": "curated-profile",
"title": "Authentik",
"repository": "https://github.com/goauthentik/authentik",
"description": "Authentik is an open-source identity provider and access platform. This recipe follows the official three-service deployment with PostgreSQL, the server and a worker, each in its own Proxmox OCI LXC.",
"website": "https://goauthentik.io/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/authentik.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": "2026-10-21",
"main_image": "ghcr.io/goauthentik/server:2026.8.5",
"category": "security",
"category_label": "Authentication & Security",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/authentik.json",
"template_id": "image-authentik",
"template": "apps/authentik.json",
"template_status": "generated-unvalidated",
"hidden": false,
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": true
},
{ {
"id": "autobrr", "id": "autobrr",
"source_app_id": "autobrr", "source_app_id": "autobrr",
@@ -18976,7 +19009,7 @@
"skipped": [] "skipped": []
}, },
"curated_profiles": { "curated_profiles": {
"included_count": 17 "included_count": 18
} }
} }
} }
+143
View File
@@ -0,0 +1,143 @@
"""Authentik keeps the vendor's three-service deployment as native OCI LXCs."""
import json
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
class AuthentikRecipeTests(unittest.TestCase):
def setUp(self):
self.curated = json.loads(
(ROOT / "catalog/curated/authentik.json").read_text(encoding="utf-8")
)
self.generated = json.loads(
(ROOT / "catalog/apps/authentik.json").read_text(encoding="utf-8")
)
self.services = {
service["name"]: service
for service in self.curated["compose_stack"]["services"]
}
def test_generated_catalog_copy_matches_curated_profile(self):
self.assertEqual(self.curated, self.generated)
def test_official_three_service_stack_uses_pinned_images_and_order(self):
self.assertEqual(set(self.services), {"postgresql", "server", "worker"})
self.assertEqual(self.curated["compose_stack"]["main_service"], "server")
self.assertEqual(
self.services["server"]["image"], "ghcr.io/goauthentik/server:2026.8.5"
)
self.assertEqual(
self.services["worker"]["image"], "ghcr.io/goauthentik/server:2026.8.5"
)
self.assertEqual(
self.services["postgresql"]["image"],
"docker.io/library/postgres:16-alpine",
)
self.assertEqual(self.services["server"]["depends_on"], ["postgresql"])
self.assertEqual(self.services["worker"]["depends_on"], ["postgresql"])
def test_generated_database_and_application_secrets_are_shared_correctly(self):
services = {
name: service["compose"].get("environment", {})
for name, service in self.services.items()
}
self.assertEqual(
services["postgresql"]["POSTGRES_PASSWORD"], "${GENERATED_PG_PASS}"
)
for name in ("server", "worker"):
self.assertEqual(
services[name]["AUTHENTIK_POSTGRESQL__PASSWORD"],
"${GENERATED_PG_PASS}",
)
self.assertEqual(
services[name]["AUTHENTIK_SECRET_KEY"],
"${GENERATED_AUTHENTIK_SECRET_KEY}",
)
generated = self.curated["compose_stack"]["installer_inputs"][
"generated_secrets"
]
self.assertEqual({secret["id"] for secret in generated}, {
"pg-pass",
"authentik-secret-key",
})
def test_persistent_mounts_and_database_healthcheck_are_preserved(self):
mounts = {
name: service["compose"].get("volumes", [])
for name, service in self.services.items()
}
self.assertIn(
{"source": "database", "target": "/var/lib/postgresql/data", "read_only": False},
mounts["postgresql"],
)
self.assertIn(
{"source": "./data", "target": "/data", "read_only": False},
mounts["server"],
)
self.assertIn(
{"source": "./data", "target": "/data", "read_only": False},
mounts["worker"],
)
self.assertIn(
{"source": "./custom-templates", "target": "/templates", "read_only": False},
mounts["server"],
)
self.assertIn(
{"source": "./custom-templates", "target": "/templates", "read_only": False},
mounts["worker"],
)
self.assertIn(
{"source": "./certs", "target": "/certs", "read_only": False},
mounts["worker"],
)
check = self.services["postgresql"]["compose"]["healthcheck"]["test"]
self.assertEqual(
check,
["CMD-SHELL", "pg_isready -h 127.0.0.1 -d authentik -U authentik"],
)
def test_cross_lxc_data_and_templates_are_host_shared_and_backed_up_separately(self):
shared = [
item
for item in self.curated["compose_stack"]["storage"]
if item["container_path"] in ("/data", "/templates")
]
self.assertEqual(len(shared), 4)
self.assertTrue(all(item["mode"] == "host-bind" for item in shared))
self.assertTrue(all(item["shared_with_other_lxc"] for item in shared))
self.assertTrue(all(item["backup"] is False for item in shared))
self.assertTrue(
all(
volume["default"] == "host-bind"
and volume["managed_volume"]["backup"] is False
for volume in self.curated["container_contract"]["volumes"]
)
)
def test_docker_socket_is_not_mounted_and_both_web_ports_are_exposed(self):
all_mounts = [
mount
for service in self.services.values()
for mount in service["compose"].get("volumes", [])
]
self.assertFalse(any("docker.sock" in mount for mount in all_mounts))
self.assertEqual(
self.services["server"]["compose"]["ports"],
["9000:9000", "9443:9443"],
)
def test_first_run_discloses_the_initial_admin_setup_without_faking_a_password(self):
first_run = self.curated["first_run"]
self.assertEqual(first_run["endpoints"][0]["port"], 9000)
credential = first_run["credentials"][0]
self.assertEqual(credential["username"], "akadmin")
self.assertIsNone(credential["password"])
self.assertTrue(credential["change_required"])
if __name__ == "__main__":
unittest.main()