Compose gates web, worker and cron on the seeder via depends_on
service_completed_successfully. Swarm ignores depends_on, so nothing
waited for the one-shot seeder there. In CI (store-i18n run 40979252521,
OpenProject#0 swarm) an overlay flap reset the seeder's Postgres
connection; Postgres rolled back RootSeeder's transaction, but the
seeder never noticed and sat idle on the dead socket forever
("Executing database seed..."). No UserCustomFieldSection rows were
committed, so /my/account rendered without fieldsets and the deploy
still reported green.
- 01_manager_ops.yml: in swarm, after the handler flush deploys the
stack, poll job_state.sh on the seeder service until its newest task
is Complete (fail on Shutdown, give up after 20 minutes), mirroring
web-app-zammad's init-job wait. Compose keeps relying on depends_on.
- env.j2: add libpq TCP keepalives (idle 60s, interval 10s, count 6)
and tcp_user_timeout=120000 to DATABASE_URL, so a connection whose
peer vanished errors out within about two minutes instead of hanging.
The seeder then exits non-zero and swarm's on-failure restart policy
reruns the seed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenProject
Description
Transform your project management with OpenProject, a vibrant and collaborative tool that brings clarity and energy to your planning, tracking, and team communication. Experience streamlined workflows and an innovative platform that propels your projects forward.
Overview
Designed for simplicity, this role automates everything needed to run OpenProject in a containerized environment. It configures essential services such as the application itself, a PostgreSQL database, reverse proxy, and optional LDAP integration for identity management.
Cosmos
The diagram places OpenProject in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).
flowchart LR
subgraph deps [Dependencies]
dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
dep_svc_db_openldap["svc-db-openldap 🐳🐝"]
dep_svc_db_postgres["svc-db-postgres 🐳🐝"]
dep_svc_net_tor["svc-net-tor 🐳🐝"]
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
dep_web_app_matomo["web-app-matomo 🐳🐝"]
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
dep_web_app_seaweedfs["web-app-seaweedfs 🐳🐝"]
dep_web_app_stalwart["web-app-stalwart 🐳🐝"]
dep_web_svc_css["web-svc-css 💻"]
dep_web_svc_logout["web-svc-logout 🐳🐝"]
end
subgraph role [web-app-openproject 🐳🐝]
svc_logout["logout"]
svc_ldap["ldap"]
svc_dashboard["dashboard"]
svc_matomo["matomo"]
svc_email["email"]
svc_postgres["postgres"]
svc_minio["minio ❌"]
svc_seaweedfs["seaweedfs"]
svc_web["web"]
svc_seeder["seeder"]
svc_cron["cron"]
svc_worker["worker"]
svc_proxy["proxy"]
svc_cache["cache"]
svc_sso["sso"]
svc_css["css"]
svc_prometheus["prometheus"]
svc_tor["tor"]
svc_openproject["openproject"]
svc_container_backup["container_backup"]
end
subgraph dependents [Dependents]
dpt_web_app_nextcloud["web-app-nextcloud 🐳🐝"]
end
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
dep_svc_db_openldap -. "0..1" .-> svc_ldap
dep_svc_db_postgres -. "0..1" .-> svc_postgres
dep_svc_net_tor -. "0..1" .-> svc_tor
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
dep_web_app_keycloak -. "0..1" .-> svc_sso
dep_web_app_matomo -. "0..1" .-> svc_matomo
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
dep_web_app_seaweedfs -. "0..1" .-> svc_seaweedfs
dep_web_app_stalwart -. "0..1" .-> svc_email
dep_web_svc_css -. "0..1" .-> svc_css
dep_web_svc_logout -. "0..1" .-> svc_logout
svc_logout -. "0..1" .-> dpt_web_app_nextcloud
Solid 1:1 edges are fixed relationships; dashed 0..1 edges are conditional (enabled only in matching deployments); red 0..0 edges are turned off in this role. Node markers show the role's deploy modes (💻 host, 🐳 compose, 🐝 swarm); ❌ marks a service that is explicitly turned off, and ⚙️ an Ansible role dependency declared in meta/main.yml.
Purpose
The purpose of this role is to reduce the complexity of setting up OpenProject with modern production-ready defaults. By combining Docker Compose and Ansible automation, it enables a hands-off setup for both small teams and larger internal infrastructures.
Features
-
Work Package Management: Create, assign, track, and prioritize tasks and issues with customizable workflows that keep your project organized and on schedule.
-
Gantt Charts & Timelines: Visualize project schedules and dependencies using intuitive Gantt charts and timeline views, enabling effective planning and resource allocation.
-
Agile Boards & Scrum/Kanban Integration: Manage agile projects using Scrum or Kanban boards, track progress through sprints, and maintain a clear overview of work in progress.
-
Time Tracking & Cost Management: Record time spent on tasks to monitor productivity and generate detailed cost reports to manage budgets effectively.
-
Collaboration & Document Management: Facilitate team collaboration with built-in discussion forums, document sharing, and version control, ensuring all project documentation remains up to date.
-
Robust Reporting & Dashboards: Gain insights through comprehensive reporting features and customizable dashboards that help monitor project performance and key metrics.
-
Custom Plugins & Extensibility: Extend functionality with a wide variety of plugins and integrations, or create your own to tailor OpenProject to your unique workflow.
-
Role-Based Access Control & Security: Manage user permissions precisely to ensure that sensitive information and critical functions remain secure.
Quick Setup
Development
Clone, set up the workstation, and deploy OpenProject onto the local stack:
git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-app-openproject full_cycle=false
Production
Run the published image to provision the inventory and deploy OpenProject to a managed server (the mounted volume persists the inventory):
APP=web-app-openproject
HOST=<your-server>
DOMAIN=<your-domain>
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"
docker run --rm -it \
-v "$PWD/inventories:/etc/infinito.nexus/inventories" \
-e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
ghcr.io/infinito-nexus/core/debian bash -c '
INVENTORY=/etc/infinito.nexus/inventories/production
infinito administration inventory provision "$INVENTORY" \
--inventory-file "$INVENTORY/devices.yml" \
--host "$HOST" \
--include "$APP" \
--vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
infinito administration deploy dedicated "$INVENTORY/devices.yml" \
--password-file "$INVENTORY/.password" \
--diff -vv'
Developer Notes
To inspect and modify live settings inside the container, open a shell and use the Rails console for full LDAP and SMTP configuration.
Persona contract opt-outs
The administrator persona is not blocked: tasks/04_admin.yml seeds a native OpenProject account whose password is the Keycloak administrator secret, so the journey runs in every variant. The biber persona is blocked only in the sso: false variants — OpenProject is seeded with the administrator alone, and biber is auto-provisioned exclusively through the oauth2-proxy trusted-header bridge onto the LDAP auth source, which those variants remove. Both flags are declared explicitly in templates/playwright.env.j2.
Credits
Implemented by Kevin Veen-Birkenbach. Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).