# Single-VM deployment 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. 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, as an on-demand utility for certificate issuance and renewal 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 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 to VM port 8080. If IPv6 is published, it must reach this same VM. ## 2. Install the repository ```sh sudo mkdir -p /YOUR/INSTALL/PARENT sudo git clone GITEA-REPOSITORY-URL /YOUR/INSTALL/PARENT/TA-Deployment-Access sudo chown -R 1000:1000 /YOUR/INSTALL/PARENT/TA-Deployment-Access cd /YOUR/INSTALL/PARENT/TA-Deployment-Access cp .env.example .env 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. Replace all example domains in the TAPM variables. Generate the cookie secret with: ```sh openssl rand -base64 48 ``` Runtime state is deliberately visible below the checkout: ```text config/ ├── broker/ # tapm.db and SQLite WAL files ├── gitea/ │ ├── config/ # app.ini and Gitea configuration │ └── data/ # repositories, packages, and gitea.db ├── letsencrypt/ # account data, certificate, and private key ├── ssl/ # optional administrator-provided certificate ├── certbot-webroot/ # HTTP-01 challenge files └── backups/ # local offline snapshots ``` The contents are ignored by Git. They must never be committed. ## 3. Bootstrap Gitea and TLS ```sh ./manage.sh bootstrap ``` This prepares the runtime directories, creates the private Docker network, starts Gitea and the HTTP-only Nginx configuration, obtains a certificate when using Let's Encrypt, and switches Nginx to TLS. For an administrator-provided certificate instead, place the certificate and key in `config/ssl/` and set: ```dotenv SSL_MODE=custom SSL_CERTIFICATE_DIR=./config/ssl SSL_CERTIFICATE_FILE=fullchain.pem SSL_CERTIFICATE_KEY_FILE=privkey.pem ``` The certificate must cover both `BROKER_DOMAIN` and `GITEA_DOMAIN`. With custom mode, `bootstrap` skips Certbot and `renew` is intentionally unavailable; replace the files through the certificate provider's process and restart or reload Nginx. `SSL_CERTIFICATE_DIR` can point at another directory relative to the repository or at an absolute host path. Create the initial Gitea administrator: ```sh docker compose exec gitea \ gitea admin user create \ --admin --username taiadmin --email ADMIN-EMAIL \ --password 'TEMPORARY-RANDOM-PASSWORD' --must-change-password ``` Sign in to Gitea at `https://GITEA_DOMAIN`, create the `TAI` organization, and create: 1. `tapm-packages`, with a read-only package token. 2. `tapm-publisher`, with a write package token. 3. An OAuth2 application whose callback is `https://BROKER_DOMAIN/auth/callback`. Place those token and OAuth values in `.env`. The broker cannot start until all required credentials are present. ## 4. Start the complete deployment ```sh ./manage.sh start ./manage.sh status curl --fail "https://BROKER_DOMAIN/health/ready" 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 For each existing repository, create an empty matching repository in the new Gitea and mirror all refs: ```sh git clone --mirror OLD-REPOSITORY-URL cd REPOSITORY.git git push --mirror https://GITEA_DOMAIN/TAI/REPOSITORY.git ``` Update developer remotes, CI credentials, submodules, documentation, and the Go module path only if the hostname embedded in the module path is also changing. Move the deployment repository last so this checkout remains updateable during the transition. ## 6. Renewal and backups When `SSL_MODE=letsencrypt`, run renewal twice daily from root's crontab: ```cron 17 3,15 * * * cd /YOUR/INSTALL/PARENT/TA-Deployment-Access && ./manage.sh renew ``` Create an application-consistent local snapshot with: ```sh ./manage.sh backup ``` The backup briefly stops both SQLite writers. Copy `config/backups/` to storage outside the VM. A backup left only on this VM does not protect against VM or datacenter loss. Also back up `.env` through a secrets-aware system. ## 7. Updates ```sh cd /YOUR/INSTALL/PARENT/TA-Deployment-Access ./update.sh ``` The updater operates only on the current VM. It fast-forwards the checked-out branch, pulls the pinned Gitea image, rebuilds and restarts the local stack, and waits for the local broker to become healthy. It has no peer discovery, SSH, or multi-node update behavior. Pin image versions as supplied and review release notes before changing them. Never change Gitea between rootless and rootful image families in place.