diff --git a/.env.example b/.env.example index 9549401..1293563 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,10 @@ GITEA_DOMAIN=git.example.com LETSENCRYPT_EMAIL=admin@example.com HTTP_PORT=8680 HTTPS_PORT=8643 -GITEA_HTTP_PORT=3000 + +# Used only with compose.direct.yaml or ./manage.sh start-direct. +BROKER_DIRECT_PORT=8080 +GITEA_DIRECT_PORT=3000 # letsencrypt: manage a certificate automatically with Certbot. # custom: read the named certificate files from ./config/ssl. diff --git a/README.md b/README.md index d8c2725..652015d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ The production layout is a self-contained, single-VM Docker deployment: No external database or load balancer is required. The datacenter edge only needs to forward public TCP 80 and 443 to the configured Docker VM ports. +All services are managed by one Compose project while remaining separate +containers for independent health checks and upgrades. See [docs/deployment.md](docs/deployment.md) for the installation, Gitea setup, TLS bootstrap, repository move, renewal, backup, and update procedures. The @@ -25,6 +27,7 @@ ProxMenu integration contract is in [docs/client-api.md](docs/client-api.md). ```sh GOCACHE=/tmp/tapm-go-cache go test ./... docker compose --env-file .env.example config -docker compose --env-file .env.example -f deploy/gitea/compose.yaml config +docker compose --env-file .env.example \ + -f compose.yaml -f compose.tls.yaml -f compose.direct.yaml config docker build -t tai/tapm-deployment-broker:local . ``` diff --git a/compose.direct.yaml b/compose.direct.yaml new file mode 100644 index 0000000..3179fe2 --- /dev/null +++ b/compose.direct.yaml @@ -0,0 +1,9 @@ +# Optional testing override. Do not use on an Internet-facing production VM. +services: + broker: + ports: + - "${BROKER_DIRECT_PORT:-8080}:8080" + + gitea: + ports: + - "${GITEA_DIRECT_PORT:-3000}:3000" diff --git a/compose.yaml b/compose.yaml index 3daae12..2b1d272 100644 --- a/compose.yaml +++ b/compose.yaml @@ -28,7 +28,37 @@ services: retries: 3 start_period: 15s + gitea: + container_name: tapm-gitea + image: docker.gitea.com/gitea:1.26.4-rootless + env_file: + - path: .env + required: false + restart: unless-stopped + networks: + - edge + volumes: + - ./config/gitea/data:/var/lib/gitea + - ./config/gitea/config:/etc/gitea + environment: + GITEA__database__DB_TYPE: sqlite3 + GITEA__database__PATH: /var/lib/gitea/data/gitea.db + GITEA__server__DOMAIN: ${GITEA_DOMAIN} + GITEA__server__ROOT_URL: https://${GITEA_DOMAIN}/ + GITEA__server__DISABLE_SSH: "true" + GITEA__service__DISABLE_REGISTRATION: "true" + GITEA__security__INSTALL_LOCK: "true" + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:3000/api/healthz"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 30s + nginx: + container_name: tapm-nginx image: nginx:1.28-alpine restart: unless-stopped networks: diff --git a/deploy/gitea/compose.yaml b/deploy/gitea/compose.yaml deleted file mode 100644 index 04e6eb9..0000000 --- a/deploy/gitea/compose.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: tapm-gitea - -services: - gitea: - container_name: tapm-gitea - image: docker.gitea.com/gitea:1.26.4-rootless - restart: unless-stopped - env_file: - - path: ../../.env - required: false - networks: - - edge - ports: - - "${GITEA_HTTP_PORT:-3000}:3000" - volumes: - - ../../config/gitea/data:/var/lib/gitea - - ../../config/gitea/config:/etc/gitea - environment: - GITEA__database__DB_TYPE: sqlite3 - GITEA__database__PATH: /var/lib/gitea/data/gitea.db - GITEA__server__DOMAIN: ${GITEA_DOMAIN} - GITEA__server__ROOT_URL: https://${GITEA_DOMAIN}/ - GITEA__server__DISABLE_SSH: "true" - GITEA__service__DISABLE_REGISTRATION: "true" - GITEA__security__INSTALL_LOCK: "true" - security_opt: - - no-new-privileges:true - healthcheck: - test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:3000/api/healthz"] - interval: 30s - timeout: 5s - retries: 5 - start_period: 30s - -networks: - edge: - external: true - name: tapm-edge diff --git a/docs/deployment.md b/docs/deployment.md index a3fe15f..8fdf2f1 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -4,24 +4,23 @@ The deployment is relocatable. Compose paths and management commands are resolved relative to the repository checkout, so it can live anywhere on the VM. `/opt/idssys/TA-Deployment-Access` is only one possible example. -It consists of four containers: +One Compose project manages: - TAPM broker, with an embedded SQLite database - Gitea, with its own embedded SQLite database and repository storage - Nginx, terminating TLS for the broker and Gitea -- Certbot, run on demand for certificate issuance and renewal +- Certbot, as an on-demand utility for certificate issuance and renewal -The configured Nginx HTTP/HTTPS ports and Gitea HTTP port are published. They -default to 8680, 8643, and 3000. Inside Docker, Nginx continues to listen on -ports 80 and 443 and Gitea listens on port 3000. Gitea SSH is disabled. +In production, only the configured Nginx HTTP and HTTPS ports are published. +They default to 8680 and 8643. Inside Docker, Nginx listens on ports 80 and 443, +the broker on 8080, and Gitea on 3000. Gitea SSH is disabled. ## 1. VM and network Install Docker Engine with Compose v2. Permit the selected HTTP and HTTPS ports and forward public TCP 80 and 443 from the datacenter edge to them. The -`GITEA_HTTP_PORT` binding is intended for direct local or management access and -should be firewalled from untrusted networks. Create public DNS A/AAAA records -for both application names before requesting the certificate. +Create public DNS A/AAAA records for both application names before requesting +the certificate. Public port 80 must reach the container's port 80 for HTTP-01 certificate renewal. For example, if `HTTP_PORT=8080`, the edge must forward public port 80 @@ -41,9 +40,8 @@ chmod 600 .env Set `BROKER_DOMAIN`, `GITEA_DOMAIN`, and `LETSENCRYPT_EMAIL` first. `HTTP_PORT` and `HTTPS_PORT` control the VM-side Docker bindings and default to 8680 and 8643. The datacenter edge should therefore forward public ports 80 and 443 to -VM ports 8680 and 8643. `GITEA_HTTP_PORT` controls the host binding for Gitea's -port 3000 and defaults to 3000. Replace all example domains in the TAPM -variables. Generate the cookie secret with: +VM ports 8680 and 8643. Replace all example domains in the TAPM variables. +Generate the cookie secret with: ```sh openssl rand -base64 48 @@ -94,7 +92,7 @@ the repository or at an absolute host path. Create the initial Gitea administrator: ```sh -docker compose --env-file .env -f deploy/gitea/compose.yaml exec gitea \ +docker compose exec gitea \ gitea admin user create \ --admin --username taiadmin --email ADMIN-EMAIL \ --password 'TEMPORARY-RANDOM-PASSWORD' --must-change-password @@ -121,6 +119,24 @@ curl --fail "https://GITEA_DOMAIN/api/healthz" ``` The broker automatically applies SQLite schema migrations before starting. +Gitea uses its own SQLite database at +`config/gitea/data/data/gitea.db`. + +For local testing that bypasses Nginx, set `BROKER_DIRECT_PORT` and +`GITEA_DIRECT_PORT` in `.env`, then run: + +```sh +./manage.sh start-direct +``` + +This adds host mappings to the broker's port 8080 and Gitea's port 3000. Do not +use the direct-port override on an Internet-facing production VM. Return to the +production port layout with: + +```sh +./manage.sh start +docker compose -f compose.yaml -f compose.tls.yaml up -d --force-recreate broker gitea +``` ## 5. Move repositories diff --git a/manage.sh b/manage.sh index 74c3b03..25bbb6e 100755 --- a/manage.sh +++ b/manage.sh @@ -76,10 +76,6 @@ prepare() { docker network create tapm-edge >/dev/null } -gitea_compose() { - docker compose --env-file .env -f deploy/gitea/compose.yaml "$@" -} - case "${1:-}" in prepare) require_env @@ -88,8 +84,7 @@ prepare) bootstrap) require_env prepare - gitea_compose up -d - docker compose up -d nginx + docker compose up -d gitea nginx if [[ "$SSL_MODE" == "letsencrypt" ]]; then docker compose --profile tools run --rm certbot certonly \ --webroot --webroot-path /var/www/certbot \ @@ -103,9 +98,17 @@ bootstrap) start) require_env prepare - gitea_compose up -d docker compose -f compose.yaml -f compose.tls.yaml up -d --build ;; +start-direct) + require_env + prepare + docker compose \ + -f compose.yaml \ + -f compose.tls.yaml \ + -f compose.direct.yaml \ + up -d --build + ;; renew) require_env [[ "$SSL_MODE" == "letsencrypt" ]] || @@ -120,19 +123,17 @@ backup) destination="config/backups/${stamp}" mkdir -p "$destination" docker compose stop broker - gitea_compose stop gitea + docker compose stop gitea cp -a config/broker "$destination/" cp -a config/gitea "$destination/" - gitea_compose start gitea - docker compose -f compose.yaml -f compose.tls.yaml start broker + docker compose -f compose.yaml -f compose.tls.yaml start gitea broker printf 'Backup created at %s\n' "$destination" ;; status) docker compose -f compose.yaml -f compose.tls.yaml ps - gitea_compose ps ;; *) - printf 'Usage: %s {prepare|bootstrap|start|renew|backup|status}\n' "$0" + printf 'Usage: %s {prepare|bootstrap|start|start-direct|renew|backup|status}\n' "$0" exit 2 ;; esac diff --git a/update.sh b/update.sh index 997260e..739c118 100755 --- a/update.sh +++ b/update.sh @@ -54,7 +54,7 @@ if ((post_pull == 0)); then fi printf 'Updating the local Gitea image and deployment stack...\n' -docker compose --env-file .env -f deploy/gitea/compose.yaml pull +docker compose --env-file .env pull gitea nginx certbot "${DEPLOY_ROOT}/manage.sh" start container_id="$(docker compose -f compose.yaml -f compose.tls.yaml ps -q broker)" @@ -72,7 +72,6 @@ for ((attempt = 1; attempt <= 75; attempt++)); do healthy) printf ' healthy.\n' docker compose -f compose.yaml -f compose.tls.yaml ps - docker compose --env-file .env -f deploy/gitea/compose.yaml ps printf 'Local deployment updated successfully.\n' exit 0 ;;