From 827f577c85d9060f988d313dfdc72f07095a6ecd Mon Sep 17 00:00:00 2001 From: Jan Sandbrink Date: Mon, 26 Jan 2026 10:30:48 +0100 Subject: [PATCH] Allow to change TLD for docker dev stack Instead of assuming that the TLD is .local, we allow to overwrite it with different TLDs, so that the local setup can be adapted to developer needs. --- .env.example | 3 ++ docker-compose.yml | 2 +- docker/dev/gitlab/.env | 1 + docker/dev/gitlab/docker-compose.yml | 4 +-- docker/dev/hocuspocus/.env | 1 + docker/dev/hocuspocus/docker-compose.yml | 4 +-- docker/dev/keycloak/.env | 1 + docker/dev/keycloak/docker-compose.yml | 6 ++-- docker/dev/minio/.env | 1 + docker/dev/minio/docker-compose.yml | 4 +-- docker/dev/nextcloud/.env | 1 + docker/dev/nextcloud/docker-compose.yml | 2 +- docker/dev/tls/.env | 1 + .../docker-compose.core-override.example.yml | 18 +++++------ .../tls/docker-compose.override.example.yml | 19 +++--------- docker/dev/tls/docker-compose.yml | 16 ++++++++++ docker/dev/tls/traefik.yaml | 7 +++++ .../development-environment/docker/README.md | 30 ++++++++++++------- 18 files changed, 75 insertions(+), 46 deletions(-) create mode 100644 docker/dev/gitlab/.env create mode 100644 docker/dev/hocuspocus/.env create mode 100644 docker/dev/keycloak/.env create mode 100644 docker/dev/minio/.env create mode 100644 docker/dev/nextcloud/.env create mode 100644 docker/dev/tls/.env diff --git a/.env.example b/.env.example index 21a60e60d1f..95285979747 100644 --- a/.env.example +++ b/.env.example @@ -44,6 +44,9 @@ PORT=3000 FE_HOST=localhost FE_PORT=4200 +# Default TLD for docker dev stack (e.g. when set to "local", services will be openproject.local, nextcloud.local, etc.) +OPENPROJECT_DOCKER_DEV_TLD=local + # Use this variables to configure hostnames for frontend and backend, e.g. to enable HTTPS in docker development setup OPENPROJECT_DEV_HOST=localhost OPENPROJECT_DEV_URL=http://${OPENPROJECT_DEV_HOST}:${FE_PORT} diff --git a/docker-compose.yml b/docker-compose.yml index 5e1c57b92a2..2e1c64f2c3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,7 +87,7 @@ services: networks: - network environment: - __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.local" + __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}" ports: - "${FE_PORT:-4200}:4200" diff --git a/docker/dev/gitlab/.env b/docker/dev/gitlab/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/gitlab/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/gitlab/docker-compose.yml b/docker/dev/gitlab/docker-compose.yml index 7389f4ed4eb..42ad0f3eb6a 100644 --- a/docker/dev/gitlab/docker-compose.yml +++ b/docker/dev/gitlab/docker-compose.yml @@ -19,10 +19,10 @@ services: networks: - external extra_hosts: - - "openproject.local:host-gateway" + - "openproject.${OPENPROJECT_DOCKER_DEV_TLD}:host-gateway" labels: - "traefik.enable=true" - - "traefik.http.routers.gitlab.rule=Host(`gitlab.local`)" + - "traefik.http.routers.gitlab.rule=Host(`gitlab.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.gitlab.entrypoints=websecure" - "traefik.http.services.gitlab.loadbalancer.server.port=80" diff --git a/docker/dev/hocuspocus/.env b/docker/dev/hocuspocus/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/hocuspocus/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/hocuspocus/docker-compose.yml b/docker/dev/hocuspocus/docker-compose.yml index 3888ab9e86b..4419c6e4365 100644 --- a/docker/dev/hocuspocus/docker-compose.yml +++ b/docker/dev/hocuspocus/docker-compose.yml @@ -5,7 +5,7 @@ services: image: openproject/hocuspocus:latest labels: - "traefik.enable=true" - - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.local`)" + - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.hocuspocus.service=hocuspocus-service" - "traefik.http.routers.hocuspocus.tls=true" - "traefik.http.services.hocuspocus-service.loadbalancer.server.port=1234" @@ -14,7 +14,7 @@ services: networks: - gateway environment: - - ALLOWED_DOMAINS=openproject.local,localhost + - ALLOWED_DOMAINS=openproject.${OPENPROJECT_DOCKER_DEV_TLD},localhost - NODE_TLS_REJECT_UNAUTHORIZED=0 - SECRET=secret12345 networks: diff --git a/docker/dev/keycloak/.env b/docker/dev/keycloak/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/keycloak/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/keycloak/docker-compose.yml b/docker/dev/keycloak/docker-compose.yml index a8cd4d2803a..4c5bf556edc 100644 --- a/docker/dev/keycloak/docker-compose.yml +++ b/docker/dev/keycloak/docker-compose.yml @@ -35,7 +35,7 @@ services: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin - KC_DB_SCHEMA=public - - KC_HOSTNAME=keycloak.local + - KC_HOSTNAME=keycloak.${OPENPROJECT_DOCKER_DEV_TLD} - KC_TRANSACTION_XA_ENABLED=false volumes: - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro @@ -43,10 +43,8 @@ services: - ./themes/:/opt/keycloak/themes/ labels: - "traefik.enable=true" - - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.local`)" + - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.keycloak-sub-secure.entrypoints=websecure" - - "traefik.http.routers.keycloak-sub-secure.tls=true" - - "traefik.http.routers.keycloak-sub-secure.tls.certresolver=step" depends_on: - db-keycloak diff --git a/docker/dev/minio/.env b/docker/dev/minio/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/minio/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/minio/docker-compose.yml b/docker/dev/minio/docker-compose.yml index 778277abfc6..cecc8ee8df9 100644 --- a/docker/dev/minio/docker-compose.yml +++ b/docker/dev/minio/docker-compose.yml @@ -19,13 +19,13 @@ services: - "traefik.enable=true" # MinIO API - "traefik.http.routers.minio.entrypoints=websecure" - - "traefik.http.routers.minio.rule=Host(`minio.local`)" + - "traefik.http.routers.minio.rule=Host(`minio.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.minio.service=minio" - "traefik.http.routers.minio.tls.certresolver=step" - "traefik.http.services.minio.loadbalancer.server.port=9000" # MinIO Admin Console (Management UI) - "traefik.http.routers.minioadmin.entrypoints=websecure" - - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.local`)" + - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.minioadmin.service=minioadmin" - "traefik.http.routers.minioadmin.tls.certresolver=step" - "traefik.http.services.minioadmin.loadbalancer.server.port=9001" diff --git a/docker/dev/nextcloud/.env b/docker/dev/nextcloud/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/nextcloud/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/nextcloud/docker-compose.yml b/docker/dev/nextcloud/docker-compose.yml index 9db2fa4ef1a..100a37dbc49 100644 --- a/docker/dev/nextcloud/docker-compose.yml +++ b/docker/dev/nextcloud/docker-compose.yml @@ -11,7 +11,7 @@ services: # - ../nextcloud_apps:/var/www/html/custom_apps labels: - "traefik.enable=true" - - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.local`)" + - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.nextcloud.entrypoints=websecure" cron: diff --git a/docker/dev/tls/.env b/docker/dev/tls/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/tls/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/tls/docker-compose.core-override.example.yml b/docker/dev/tls/docker-compose.core-override.example.yml index cf33272417c..d45a9be091b 100644 --- a/docker/dev/tls/docker-compose.core-override.example.yml +++ b/docker/dev/tls/docker-compose.core-override.example.yml @@ -6,30 +6,30 @@ x-op-env-override: &environment SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt # uncomment and set all the envs below to integrate keycloak with OpenProject # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_DISPLAY__NAME: Keycloak - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.local - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.local + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.${OPENPROJECT_DOCKER_DEV_TLD} + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.${OPENPROJECT_DOCKER_DEV_TLD} # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_SECRET: - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_ISSUER: https://keycloak.local/realms/ + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_ISSUER: https://keycloak.${OPENPROJECT_DOCKER_DEV_TLD}/realms/ # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_AUTHORIZATION__ENDPOINT: /realms//protocol/openid-connect/auth # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_TOKEN__ENDPOINT: /realms//protocol/openid-connect/token # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_USERINFO__ENDPOINT: /realms//protocol/openid-connect/userinfo - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_END__SESSION__ENDPOINT: https://keycloak.local/realms//protocol/openid-connect/logout + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_END__SESSION__ENDPOINT: https://keycloak.${OPENPROJECT_DOCKER_DEV_TLD}/realms//protocol/openid-connect/logout # uncomment the following for using minio (local S3) as file storage with TLS support: # OPENPROJECT_ATTACHMENTS__STORAGE: "fog" # OPENPROJECT_FOG_DIRECTORY: "openproject-uploads" # OPENPROJECT_FOG_CREDENTIALS_PROVIDER: "AWS" # Minio is S3 compliant, so we can use the AWS provider - # OPENPROJECT_FOG_CREDENTIALS_ENDPOINT: "https://minio.local" + # OPENPROJECT_FOG_CREDENTIALS_ENDPOINT: "https://minio.${OPENPROJECT_DOCKER_DEV_TLD}" # OPENPROJECT_FOG_CREDENTIALS_AWS__ACCESS__KEY__ID: "minioadmin" # OPENPROJECT_FOG_CREDENTIALS_AWS__SECRET__ACCESS__KEY: "minioadmin" # OPENPROJECT_FOG_CREDENTIALS_PATH__STYLE: "true" # OPENPROJECT_FOG_CREDENTIALS_REGION: "us-east-1" - # OPENPROJECT_DEV_EXTRA_HOSTS: "${OPENPROJECT_DEV_HOST},minio.local" + # OPENPROJECT_DEV_EXTRA_HOSTS: "${OPENPROJECT_DEV_HOST},minio.${OPENPROJECT_DOCKER_DEV_TLD}" services: backend: environment: <<: *environment - OPENPROJECT_CLI_PROXY: "https://openproject-assets.local" + OPENPROJECT_CLI_PROXY: "https://openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}" networks: - external volumes: @@ -42,7 +42,7 @@ services: # - ~/.step/certs:/usr/local/share/ca-certificates labels: - "traefik.enable=true" - - "traefik.http.routers.openproject.rule=Host(`openproject.local`)" + - "traefik.http.routers.openproject.rule=Host(`openproject.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.openproject.entrypoints=websecure" worker: @@ -77,7 +77,7 @@ services: - external labels: - "traefik.enable=true" - - "traefik.http.routers.openproject-assets.rule=Host(`openproject-assets.local`)" + - "traefik.http.routers.openproject-assets.rule=Host(`openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.openproject-assets.entrypoints=websecure" # You need to define the same external network diff --git a/docker/dev/tls/docker-compose.override.example.yml b/docker/dev/tls/docker-compose.override.example.yml index 5ca5c2cd8a7..f2f6b439f5f 100644 --- a/docker/dev/tls/docker-compose.override.example.yml +++ b/docker/dev/tls/docker-compose.override.example.yml @@ -14,18 +14,7 @@ services: step: condition: service_healthy - networks: - external: - aliases: - # Traefik is aliased to all the services that run behind it here. - # The reason to do this is that step-ca tries to validate the domains - # by connecting to them, and we'd like it to go through traefik, instead - # of calling the service containers directly. - - openproject.local - - openproject-assets.local - - nextcloud.local - - gitlab.local - - keycloak.local - - hocuspocus.local - - minio.local - - minioadmin.local + # Enabling traefik for the traefik container itself will give you access to the Traefik Dashboard, which can be a useful + # tool to inspect whether everything is working fine. It's not advised to publicly expose this dashboard. + labels: + - "traefik.enable=true" diff --git a/docker/dev/tls/docker-compose.yml b/docker/dev/tls/docker-compose.yml index 5bd43639a01..1d4542081c1 100644 --- a/docker/dev/tls/docker-compose.yml +++ b/docker/dev/tls/docker-compose.yml @@ -10,6 +10,22 @@ services: - ./acme.json:/acme.json - step:/step:ro restart: unless-stopped + networks: + external: + aliases: + - traefik.${OPENPROJECT_DOCKER_DEV_TLD} + - openproject.${OPENPROJECT_DOCKER_DEV_TLD} + - openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD} + - nextcloud.${OPENPROJECT_DOCKER_DEV_TLD} + - gitlab.${OPENPROJECT_DOCKER_DEV_TLD} + - keycloak.${OPENPROJECT_DOCKER_DEV_TLD} + - hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD} + - minio.${OPENPROJECT_DOCKER_DEV_TLD} + - minioadmin.${OPENPROJECT_DOCKER_DEV_TLD} + labels: + - "traefik.http.routers.traefik.rule=Host(`traefik.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.entrypoints=websecure" step: image: smallstep/step-ca:latest diff --git a/docker/dev/tls/traefik.yaml b/docker/dev/tls/traefik.yaml index 0d511a968c6..7f19e16d581 100644 --- a/docker/dev/tls/traefik.yaml +++ b/docker/dev/tls/traefik.yaml @@ -1,9 +1,15 @@ log: level: INFO + +api: + dashboard: true + disabledashboardad: true + providers: docker: network: gateway exposedByDefault: false + entryPoints: web: address: ":80" @@ -16,6 +22,7 @@ entryPoints: http: tls: certresolver: step # Using step by default, overwritable via CLI + certificatesresolvers: step: acme: diff --git a/docs/development/development-environment/docker/README.md b/docs/development/development-environment/docker/README.md index e372b8f1e28..18b9d22921b 100644 --- a/docs/development/development-environment/docker/README.md +++ b/docs/development/development-environment/docker/README.md @@ -264,13 +264,6 @@ define for your services to your `/etc/hosts`. ::1 openproject.local openproject-assets.local traefik.local ``` -#### DNS? Where are you? - -We have plans to add a local DNS to this development setup, making two things possible: - -1. No requirement to amend your `/etc/hosts` file anymore. -2. Being accessible from another device within your internal network (e.g. a cellphone). - ### Local certificate authority We use [traefik](https://traefik.io/) as a reverse proxy and [step-ca](https://smallstep.com/docs/step-ca/) as a local @@ -457,6 +450,23 @@ to have Nextcloud running to test the Nextcloud-OpenProject integration. To do t 2. Make sure step-ca can reach it to validate it for SSH. In `docker/dev/tls/docker-compose.override.yml`, add the host to the `aliases` section of the traefik networking. +### Alternative: Using Let's encrypt + +An alternative approach is to issue certificates through Let's encrypt. This allows you to skip steps related to usage and setup +of a custom, non-trusted CA. However, it requires that you have access to a domain name that you control and requires additional +step to make the reverse proxy publicly reachable, which is not in scope of what this documentation can cover. + +If you need such a setup, you can change the `docker-compose.override.yml` for the reverse proxy, to use `letsencrypt` (see the +corresponding `docker-compose.override.example.yml`). Make sure to export an environment variable with your alternative DNS zone +before starting anything via docker compose. For example: + +```bash +export OPENPROJECT_DOCKER_DEV_TLD=dev.example.com +docker compose up -d backend frontend +``` + +Will make your containers available under openproject.dev.example.com and openproject-assets.dev.example.com respectively. + ### Troubleshooting After this setup you should be able to access your OpenProject development instance at `https://openproject.local`. If @@ -564,7 +574,7 @@ Upon setting up all the things correctly, we can see a login with `keycloak` opt ## MinIO Service (local S3 storage backend) -Within `docker/dev/minio` a compose file is provided for running a local MinIO instance with TLS support which can be used as a S3 storage for uploading files. +Within `docker/dev/minio` a compose file is provided for running a local MinIO instance with TLS support which can be used as a S3 storage for uploading files. When running with TLS support, the MinIO instance will be accessible on `https://minio.local` and a management UI (MinIO Console) will be available on `https://minioadmin.local/`. ### Running the MinIO Instance @@ -578,8 +588,8 @@ docker compose --project-directory docker/dev/minio up -d ``` This will automatically create a bucket named `openproject-uploads` which is used to store uploaded files. -If you want to use TLS support, make sure to copy and uncomment the MinIO configuration environment variables in `docker/dev/tls/docker-compose.core.override.example.yml` to your `docker-compose.override.yml` file in the project root directory. If you want to use MinIO without TLS support, make sure to copy the environment variables from `docker/dev/minio/docker-compose.core-override.example.yml` to your `docker-compose.override.yml` file (in the project root directory). -After that, hard restart the `backend` service to apply the changes: +If you want to use TLS support, make sure to copy and uncomment the MinIO configuration environment variables in `docker/dev/tls/docker-compose.core.override.example.yml` to your `docker-compose.override.yml` file in the project root directory. If you want to use MinIO without TLS support, make sure to copy the environment variables from `docker/dev/minio/docker-compose.core-override.example.yml` to your `docker-compose.override.yml` file (in the project root directory). +After that, hard restart the `backend` service to apply the changes: ``` docker compose down backend