Skip to content

feat: initial support for deploying multiple instances and VPN#99

Draft
tonsV2 wants to merge 13 commits into
masterfrom
development-2.0
Draft

feat: initial support for deploying multiple instances and VPN#99
tonsV2 wants to merge 13 commits into
masterfrom
development-2.0

Conversation

@tonsV2

@tonsV2 tonsV2 commented May 7, 2026

Copy link
Copy Markdown
Collaborator

This PR touches almost every file in the project and should be considered the base of version 2.0. The two major features it adds is support for deploying multiples instances on the same server and support for VPN (using WireGuard)

tonsV2 and others added 3 commits May 8, 2026 04:31
…s on th… (#94)

* Reapply "feat: initial support for deploying multiple instances on the same host. WIP"

This reverts commit ed9278f.

* feat: per-project backup paths

* chore: doc updates and list-instances (#96)

* chore: doc updates and list-instances

* Apply suggestions from code review - remove numbering in reference file

Co-authored-by: Philip-Larsen-Donnelly <35666657+Philip-Larsen-Donnelly@users.noreply.github.com>

* fix: review comments

* fix: add shebang to env-utils.sh to fix shellcheck SC2148

* feat: add delete-instance target to remove instance data and env file

* refactor: rename launch-* targets to start-* for consistency with stop-instance

* refactor: rename monitoring_net network to monitoring

* fix: resolve shellcheck SC1091/SC2119 warnings in generate scripts

---------

Co-authored-by: Philip-Larsen-Donnelly <phil@dhis2.org>
Co-authored-by: Philip-Larsen-Donnelly <35666657+Philip-Larsen-Donnelly@users.noreply.github.com>
…ble (DEVOPS-665) (#101)

* refactor(server-tools): drop systemd and overlay management from ansible (DEVOPS-665)

* style(server-tools): unwrap mid-sentence line breaks in README
* feat: initial WireGuard

* feat: move Grafana to VPN-only access via grafana.internal

Grafana is no longer publicly accessible. It's now only reachable at
https://grafana.internal for clients connected to the WireGuard VPN.

- Add stacks/traefik/conf.d/internal.yml with VPN-only routes for
  grafana.internal and glowroot.internal (served with mkcert wildcard cert)
- Mount overlays/wireguard/certs/ into stacks/traefik so certs are shared
  between the wireguard and traefik compose projects
- Fix wireguard overlay incompatibilities with multi-instance approach:
  use bind-mount for certs instead of named volume, change network from
  `frontend` to `proxy`, remove orphaned traefik service override that
  referenced the now-deleted single-instance dynamic.yml
- Add service definitions to overlays/wireguard/traefik/internal.yml
  (grafana-internal → monitoring-grafana:3000, glowroot-internal → app:4000)
- Remove stacks/traefik/conf.d/monitoring.yml.template and the
  start-monitoring Makefile step that generated it (public Grafana route)
- Remove GRAFANA_HOSTNAME from monitoring .env.template and generate script;
  hardcode GF_SERVER_ROOT_URL=https://grafana.internal in docker-compose.yml
- Gitignore overlays/wireguard/certs/ and config/ (contain private keys)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: proxy VPN traffic to Traefik via socat sidecar

Adds wireguard-proxy service that listens on 10.8.0.1:443 (wg0 IP) inside
the WireGuard container's network namespace and forwards to traefik:443.
Docker DNS resolves 'traefik' per connection, so it handles Traefik
container restarts without needing hardcoded IPs or iptables DNAT rules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: initial WireGuard

* chore: get-vpn-ca

* docs: vpn updates

* fix: restore inventory.ini placeholder host

* fix: remove orphan internal.yml from wireguard overlay

Unreferenced near-duplicate of stacks/traefik/conf.d/internal.yml,
which is the file Traefik actually loads.

* chore: gitignore rootCA.pem from get-vpn-ca

`make get-vpn-ca` writes the root CA to the working tree so clients
can install it; it should not land in commits.

* fix: restrict internal.key to mode 600

The mkcert container that writes the key and the Traefik container
that reads it both run as root, so the key does not need to be
world-readable.

* fix: drop unused wildcard domain env from mkcert service

The `domain: "*.internal"` env var was never consumed: the compose
file overrides the image entrypoint with `command: sh /mkcert.sh`,
and the script hardcodes the two host names. The generated cert is
already a 2-SAN cert, not a wildcard.

* style: replace em dashes with ASCII dashes

Sweep em dashes out of files added or modified on this branch.
Pre-existing em dashes in master (README intro and footer) are left
alone as out of scope.

* docs: document mkcert image digest pin

The vishnunair/docker-mkcert image only publishes a `latest` tag, so
pinning by digest is the only stable reference available.

* fix: generate one cert per internal host

Previously mkcert.sh produced a single 2-SAN cert (grafana.internal +
glowroot.internal) shared by both routes. Switch to one cert per host
and list both in Traefik's tls.certificates so SNI selects the right
one.

Existing deployments must clear the wireguard-certs volume to drop the
old internal.crt/internal.key on first run after this change.

* feat: build mkcert image locally instead of pulling pre-built

Replaces the pinned vishnunair/docker-mkcert digest with a minimal custom Dockerfile that fetches mkcert directly from the upstream release URL, removing the dependency on a third-party image.

* feat: align server-tools with VPN deployment

* fix: handle IPv6 in wireguard

* feat: simplify server deployment

* refactor: rename operator_user to docker_user

* refactor: split tunnel default

* refactor: remove obsolete env template

* fix: refactor wireguard env

* Update docs/vpn.md

Co-authored-by: radnov <radoslav@dhis2.org>

* fix: let Buildkit populate TARGETARCH for mkcert build

* fix: declare wireguard-certs volume external to avoid project ownership warning

* fix: make internal cert keys readable by Traefik

* fix: use explicit placeholder IP in example inventory

* fix: mount coredns directory to avoid macOS VirtioFS bind-mount failure

* docs: note macOS Docker Desktop UDP setting for the VPN server

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Philip-Larsen-Donnelly <phil@dhis2.org>
Co-authored-by: Philip-Larsen-Donnelly <35666657+Philip-Larsen-Donnelly@users.noreply.github.com>
Co-authored-by: radnov <radoslav@dhis2.org>
@tonsV2 tonsV2 changed the title chore: Reapply "feat: initial support for deploying multiple instance… feat: initial support for deploying multiple instances and VPN Jun 17, 2026
tonsV2 added 4 commits June 30, 2026 08:02
Copy config/dhis2 and config/postgresql defaults into instances/<PROJECT_NAME>/ on create-instance and mount the per-instance copies via COMPOSE_PROJECT_NAME so each instance can be tuned independently.
Define the proxy/monitoring networks and the wireguard-certs volume in stacks/base/networks.yml and stacks/base/volumes.yml and create them through $(DOCKER) compose so they land in the same daemon/context as the compose up, fixing the external volume not found error on hosts where docker needs sudo and on Docker Desktop.
Move the per-instance env file from instances/<name>.env to instances/<name>/.env so an instance's env and its dhis2/postgresql config share one folder, letting delete-instance remove everything with a single rm -rf and list-instances enumerate by folder.

@radnov radnov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested end-to-end on a fresh Ubuntu 24.04 VM and a MacOS client machine. The multi-instance design seems to work as expected: Ansible provisioning, shared Traefik + monitoring, WireGuard VPN, and two isolated instances all came up without errors. Then I logged into DHIS2 with the generated admin password, changed user profile data and installed an app, all without issues.

I also updated the notes for the MacOS setup, removed some dead config and updated glowroot so it's served on the internal network per instance.

P.S.
The e2e test will be fixed in a separate PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants