> ## Documentation Index
> Fetch the complete documentation index at: https://harisfazillah.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# How-To: Deploy Sovereign Gitea via Automated Ansible Playbook

> Step-by-step instructions for automated deployment of Gitea over HTTPS using rootless Podman and Ansible playbooks with high-entropy credential vaulting...

This guide details how to automate the standalone deployment of a self-hosted Gitea GitOps stack over rootless Podman using Ansible playbooks. The workflow handles OS dependencies, linger enablement, secret vaulting, 5-year Sovereign TLS certificate generation, Quadlet systemd unit deployment, and installation screen bypass automatically.

***

## 1. Overview & Architecture

Using Ansible for deployment guarantees idempotent, reproducible infrastructure provisioning.

### Key Architecture Specs

* **Default Domain / Host IP:** `10.17.250.28` (or target inventory host)
* **Database Backend:** PostgreSQL 15 (Alpine)
* **Application Server:** Gitea 1.26.3
* **HTTP/HTTPS Port:** `3000` (Mapped to container port `3000` over HTTPS)
* **SSH Port:** `2222` (Mapped to container port `22` for Git over SSH)
* **Security & TLS:** TLS certificates generated via `community.crypto` and signed by Sovereign CA.
* **Automated Bypass:** Bypasses web installation screen programmatically (`GITEA__security__INSTALL_LOCK: "true"`).

***

## 2. Prerequisites & Ansible Setup

Ensure Ansible and required collection packages are installed on the control node:

```bash theme={null}
# Install required Ansible collections for Podman and Crypto tasks
ansible-galaxy collection install containers.podman community.crypto community.general
```

Verify target inventory connectivity:

```bash theme={null}
ansible -i inventory/hosts.yml target_hosts -m ping
```

***

## 3. Ansible Role & Playbook Structure

The deployment relies on Ansible role variables and task definitions:

### Role Defaults (`roles/gitea/defaults/main.yml`)

```yaml theme={null}
gitea_domain: "10.17.250.28"
gitea_root_url: "https://10.17.250.28:3000/"
gitea_http_port: 3000
gitea_ssh_port: 2222
gitea_db_password: "{{ vault_gitea_db_password }}"
gitea_postgres_max_connections: 200
gitea_postgres_shared_buffers: "256MB"
gitea_postgres_effective_cache_size: "1GB"
gitea_postgres_maintenance_work_mem: "64MB"
gitea_postgres_work_mem: "16MB"
```

### Main Task Execution Flow (`roles/gitea/tasks/main.yml`)

```yaml theme={null}
- name: Generate and deploy TLS certificates
  ansible.builtin.import_tasks: tls.yml

- name: Ensure Quadlet directory exists
  ansible.builtin.file:
    path: ~/.config/containers/systemd/
    state: directory
    mode: '0755'

- name: Deploy Gitea Kube Quadlet YAML (mode 0600 for secrets)
  ansible.builtin.template:
    src: "gitea-stack.yaml.j2"
    dest: "~/.config/containers/systemd/gitea-stack.yaml"
    mode: '0600'

- name: Deploy Gitea Kube Quadlet Unit
  ansible.builtin.template:
    src: "gitea-stack.kube.j2"
    dest: "~/.config/containers/systemd/gitea-stack.kube"
    mode: '0644'
  notify: reload systemd user daemon

- name: Reload systemd user daemon for Quadlet generation
  ansible.builtin.shell: systemctl --user daemon-reload
  environment:
    XDG_RUNTIME_DIR: "/run/user/{{ ansible_user_uid }}"
  changed_when: false

- name: Ensure Gitea service is enabled and started
  ansible.builtin.systemd:
    name: gitea-stack.service
    state: started
    enabled: yes
    scope: user
    daemon_reload: yes
  environment:
    XDG_RUNTIME_DIR: "/run/user/{{ ansible_user_uid }}"
```

### Sovereign TLS Task Automation (`roles/gitea/tasks/tls.yml`)

```yaml theme={null}
- name: Ensure local certs directory exists
  ansible.builtin.file:
    path: "{{ playbook_dir }}/../vault/generated"
    state: directory
    mode: '0700'
  delegate_to: localhost
  run_once: true
  become: false

- name: Generate Gitea private key
  community.crypto.openssl_privatekey:
    path: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.key"
    size: 2048
  delegate_to: localhost
  become: false

- name: Generate Gitea CSR
  community.crypto.openssl_csr:
    path: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.csr"
    privatekey_path: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.key"
    common_name: "{{ inventory_hostname }}"
    subject_alt_name:
      - "IP:{{ ansible_host }}"
      - "DNS:{{ inventory_hostname }}"
  delegate_to: localhost
  become: false

- name: Sign Gitea certificate with Sovereign CA
  community.crypto.x509_certificate:
    path: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.crt"
    csr_path: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.csr"
    ownca_path: "{{ playbook_dir }}/../vault/dsom-elasticsearch-ca.crt"
    ownca_privatekey_path: "{{ playbook_dir }}/../vault/dsom-elasticsearch-ca.key"
    provider: ownca
    ownca_not_after: "+1825d"
  delegate_to: localhost
  become: false

- name: Ensure target cert directory exists
  ansible.builtin.file:
    path: "~/.config/gitea/certs"
    state: directory
    mode: '0755'
  become: false

- name: Deploy Sovereign CA certificate
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../vault/dsom-elasticsearch-ca.crt"
    dest: "~/.config/gitea/certs/ca.crt"
    mode: '0644'
  become: false

- name: Deploy Gitea certificate
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.crt"
    dest: "~/.config/gitea/certs/gitea.crt"
    mode: '0644'
  become: false

- name: Deploy Gitea key
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../vault/generated/{{ inventory_hostname }}-gitea.key"
    dest: "~/.config/gitea/certs/gitea.key"
    mode: '0644'
  become: false

- name: Install Sovereign CA to Host Trust Store (Debian family)
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../vault/dsom-elasticsearch-ca.crt"
    dest: /usr/local/share/ca-certificates/sovereign-ca.crt
    mode: '0644'
  become: true
  become_user: root
  when: ansible_os_family == 'Debian'
  notify: update ca-certificates

- name: Install Sovereign CA to Host Trust Store (Red Hat family)
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../vault/dsom-elasticsearch-ca.crt"
    dest: /etc/pki/ca-trust/source/anchors/sovereign-ca.crt
    mode: '0644'
  become: true
  become_user: root
  when: ansible_os_family == 'RedHat'
  notify: update ca-trust
```

***

## 4. Playbook Execution Commands

### Deploy Locally on Active Host

```bash theme={null}
ansible-playbook playbooks/dsom/setup_gitea.yml
```

### Deploy Remote Host Inventory

```bash theme={null}
ansible-playbook -i inventory/hosts.yml playbooks/dsom/setup_gitea.yml -e "target_hosts=gitea_production_nodes"
```

### Deploy Using Ansible Vault for Encrypted Passwords

```bash theme={null}
ansible-playbook -i inventory/hosts.yml playbooks/dsom/setup_gitea.yml --ask-vault-pass
```

***

## 5. Post-Deployment Verification

Verify that Gitea systemd service and containers are active on target host:

```bash theme={null}
# Verify systemd user service status
systemctl --user status gitea-stack.service

# Verify podman containers are running
podman pod ps
podman ps
```

***

## 6. Securing Secrets in Ansible & Git (Best Practices)

1. **Ansible Vault:** Always store database and admin credentials in encrypted vault files (`ansible-vault create`).
2. **Dynamic Ingestion:** Use `lookup('ansible.builtin.env', 'GITEA_DB_PASSWORD')` for runtime terminal injection.
3. **Repository Exclusions:** Confirm `.gitignore` includes `*.env`, `*credentials.txt`, and private key files (`*.key`).

***

*Deep State of Mind (DSOM) For My AI Protocol | Harisfazillah Jamel (LinuxMalaysia) | 2026-08-20*
*Standard: UK English | DBP-standard Bahasa Melayu Malaysia (Piawai) | GNU General Public License v3.0*


## Related topics

- [How-To: Deploy Sovereign Gitea via Rootless Podman & Quadlet](/how-to/install-gitea-podman-quadlet.md)
- [How-To Guides Index](/how-to/index.md)
- [HOWTO: eod-palace - End-of-Day Saves](/tools/howto-eod-palace.md)
- [🔄 DSOM Three-Pillar Strategy: GitOps · AIOps · Ansible (v1.0)](/governance/gitops-aiops-ansible-strategy.md)
- [HOWTO: sod-palace - Start-of-Day Ritual](/tools/howto-sod-palace.md)
