# Single-VM deployment The production deployment lives at: ```text /opt/idssys/TA-Deployment-Access ``` It consists of four containers: - 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 Only Nginx ports 80 and 443 and Gitea SSH port 2222 are published. The broker and Gitea HTTP services are reachable only on the private `tapm-edge` Docker network. ## 1. VM and network Install Docker Engine with Compose v2. Permit inbound TCP 80, 443, and 2222. Forward those ports from the datacenter edge to this VM. Create public DNS A/AAAA records for both application names before requesting the certificate. Port 80 must remain reachable for HTTP-01 certificate renewal. If IPv6 is published, it must reach this same VM. ## 2. Install the repository ```sh sudo mkdir -p /opt/idssys sudo git clone GITEA-REPOSITORY-URL /opt/idssys/TA-Deployment-Access sudo chown -R 1000:1000 /opt/idssys/TA-Deployment-Access cd /opt/idssys/TA-Deployment-Access cp .env.example .env chmod 600 .env ``` Set `BROKER_DOMAIN`, `GITEA_DOMAIN`, and `LETSENCRYPT_EMAIL` first. 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 ├── 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 one certificate covering both domains, and switches Nginx to TLS. Create the initial Gitea administrator: ```sh docker compose --env-file .env -f deploy/gitea/compose.yaml 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. ## 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 ssh://git@GITEA_DOMAIN:2222/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 Run renewal twice daily from root's crontab: ```cron 17 3,15 * * * cd /opt/idssys/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 /opt/idssys/TA-Deployment-Access git pull --ff-only docker compose --env-file .env -f deploy/gitea/compose.yaml pull ./manage.sh start ``` Pin image versions as supplied and review release notes before changing them. Never change Gitea between rootless and rootful image families in place.