Add infra setup: Terraform for DO droplet + Cloudflare DNS, Ansible roles for Gitea, Vaultwarden, and Gitea runner

This commit is contained in:
Ryan Moon
2026-03-31 06:08:21 -05:00
parent bde3ad64fd
commit fe3c7646d6
33 changed files with 6435 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
---
gitea_runner_version: "0.2.11"
gitea_runner_data_dir: /var/lib/gitea-runner
gitea_instance_url: "https://git.example.com"
# Generate in Gitea: Site Admin → Actions → Runners → Create new runner
gitea_runner_token: ""
gitea_runner_name: "{{ inventory_hostname }}"
gitea_runner_labels: "ubuntu-latest:docker://node:20"

View File

@@ -0,0 +1,6 @@
---
- name: Restart gitea-runner
community.docker.docker_compose_v2:
project_src: "{{ gitea_runner_data_dir }}"
state: present
recreate: always

View File

@@ -0,0 +1,27 @@
---
- name: Create gitea-runner data directory
file:
path: "{{ gitea_runner_data_dir }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Deploy docker-compose file
template:
src: docker-compose.yml.j2
dest: "{{ gitea_runner_data_dir }}/docker-compose.yml"
mode: "0600"
notify: Restart gitea-runner
- name: Deploy runner config
template:
src: config.yml.j2
dest: "{{ gitea_runner_data_dir }}/config.yml"
mode: "0600"
notify: Restart gitea-runner
- name: Start gitea-runner
community.docker.docker_compose_v2:
project_src: "{{ gitea_runner_data_dir }}"
state: present

View File

@@ -0,0 +1,17 @@
log:
level: info
runner:
file: /data/.runner
capacity: 2 # max concurrent jobs — lower if droplet is under load
labels:
- "ubuntu-latest:docker://node:20"
- "ubuntu-22.04:docker://node:20"
cache:
enabled: true
dir: /data/cache
container:
network: bridge
force_pull: false # reuse cached images to speed up builds

View File

@@ -0,0 +1,14 @@
services:
gitea-runner:
image: gitea/act_runner:{{ gitea_runner_version }}
container_name: gitea-runner
restart: unless-stopped
volumes:
- {{ gitea_runner_data_dir }}/config.yml:/config.yml
- {{ gitea_runner_data_dir }}/data:/data
- /var/run/docker.sock:/var/run/docker.sock # allows runner to spin up job containers
environment:
CONFIG_FILE: /config.yml
GITEA_INSTANCE_URL: "{{ gitea_instance_url }}"
GITEA_RUNNER_REGISTRATION_TOKEN: "{{ gitea_runner_token }}"
GITEA_RUNNER_NAME: "{{ gitea_runner_name }}"

View File

@@ -0,0 +1,10 @@
---
gitea_version: "1.22.3"
gitea_domain: "git.example.com"
gitea_http_port: 3000
gitea_ssh_port: 2222
gitea_data_dir: /var/lib/gitea
# Cloudflare Origin Certificate
cf_origin_cert: ""
cf_origin_key: ""

View File

@@ -0,0 +1,11 @@
---
- name: Restart gitea
community.docker.docker_compose_v2:
project_src: "{{ gitea_data_dir }}"
state: present
recreate: always
- name: Reload nginx
systemd:
name: nginx
state: reloaded

View File

@@ -0,0 +1,88 @@
---
- name: Install dependencies
apt:
name: [nginx, docker.io, docker-compose-v2]
state: present
update_cache: true
- name: Enable and start Docker
systemd:
name: docker
enabled: true
state: started
- name: Create gitea data directory
file:
path: "{{ gitea_data_dir }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Deploy docker-compose file
template:
src: docker-compose.yml.j2
dest: "{{ gitea_data_dir }}/docker-compose.yml"
mode: "0600"
notify: Restart gitea
- name: Start gitea
community.docker.docker_compose_v2:
project_src: "{{ gitea_data_dir }}"
state: present
# ─── Cloudflare Origin Certificate ───────────────────────────────────────────
- name: Create SSL directory
file:
path: /etc/nginx/ssl
state: directory
owner: root
group: root
mode: "0700"
- name: Install Cloudflare origin certificate
copy:
content: "{{ cf_origin_cert }}"
dest: /etc/nginx/ssl/cf-origin.pem
owner: root
group: root
mode: "0600"
notify: Reload nginx
- name: Install Cloudflare origin key
copy:
content: "{{ cf_origin_key }}"
dest: /etc/nginx/ssl/cf-origin.key
owner: root
group: root
mode: "0600"
notify: Reload nginx
# ─── nginx ────────────────────────────────────────────────────────────────────
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/gitea
mode: "0644"
notify: Reload nginx
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/gitea
dest: /etc/nginx/sites-enabled/gitea
state: link
notify: Reload nginx
- name: Remove nginx default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Reload nginx
- name: Ensure nginx is started
systemd:
name: nginx
enabled: true
state: started

View File

@@ -0,0 +1,19 @@
services:
gitea:
image: gitea/gitea:{{ gitea_version }}
container_name: gitea
restart: unless-stopped
volumes:
- {{ gitea_data_dir }}/data:/data
environment:
USER_UID: 1000
USER_GID: 1000
GITEA__server__DOMAIN: "{{ gitea_domain }}"
GITEA__server__ROOT_URL: "https://{{ gitea_domain }}/"
GITEA__server__HTTP_PORT: 3000
GITEA__server__SSH_DOMAIN: "{{ gitea_domain }}"
GITEA__server__SSH_PORT: "{{ gitea_ssh_port }}"
GITEA__server__START_SSH_SERVER: "true"
ports:
- "127.0.0.1:{{ gitea_http_port }}:3000"
- "{{ gitea_ssh_port }}:22"

View File

@@ -0,0 +1,24 @@
server {
listen 80;
server_name {{ gitea_domain }};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name {{ gitea_domain }};
ssl_certificate /etc/nginx/ssl/cf-origin.pem;
ssl_certificate_key /etc/nginx/ssl/cf-origin.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://127.0.0.1:{{ gitea_http_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -0,0 +1,18 @@
---
vaultwarden_domain: "vault.example.com"
vaultwarden_port: 8080
vaultwarden_data_dir: /var/lib/vaultwarden
# Set to your postgres connection string or leave as default for SQLite
vaultwarden_database_url: "" # e.g. postgresql://user:pass@host/vaultwarden
# Restrict signups after first admin account is created
vaultwarden_signups_allowed: "true"
# Admin token — set this to a strong random string
# Generate with: openssl rand -base64 48
vaultwarden_admin_token: ""
# Cloudflare Origin Certificate
cf_origin_cert: ""
cf_origin_key: ""

View File

@@ -0,0 +1,11 @@
---
- name: Restart vaultwarden
community.docker.docker_compose_v2:
project_src: "{{ vaultwarden_data_dir }}"
state: present
recreate: always
- name: Reload nginx
systemd:
name: nginx
state: reloaded

View File

@@ -0,0 +1,76 @@
---
- name: Install dependencies
apt:
name: [docker.io, docker-compose-v2]
state: present
update_cache: true
- name: Enable and start Docker
systemd:
name: docker
enabled: true
state: started
- name: Create vaultwarden data directory
file:
path: "{{ vaultwarden_data_dir }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Deploy docker-compose file
template:
src: docker-compose.yml.j2
dest: "{{ vaultwarden_data_dir }}/docker-compose.yml"
mode: "0600"
notify: Restart vaultwarden
- name: Start vaultwarden
community.docker.docker_compose_v2:
project_src: "{{ vaultwarden_data_dir }}"
state: present
# ─── Cloudflare Origin Certificate ───────────────────────────────────────────
- name: Create SSL directory
file:
path: /etc/nginx/ssl
state: directory
owner: root
group: root
mode: "0700"
- name: Install Cloudflare origin certificate
copy:
content: "{{ cf_origin_cert }}"
dest: /etc/nginx/ssl/cf-origin.pem
owner: root
group: root
mode: "0600"
notify: Reload nginx
- name: Install Cloudflare origin key
copy:
content: "{{ cf_origin_key }}"
dest: /etc/nginx/ssl/cf-origin.key
owner: root
group: root
mode: "0600"
notify: Reload nginx
# ─── nginx ────────────────────────────────────────────────────────────────────
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/vaultwarden
mode: "0644"
notify: Reload nginx
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/vaultwarden
dest: /etc/nginx/sites-enabled/vaultwarden
state: link
notify: Reload nginx

View File

@@ -0,0 +1,16 @@
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- {{ vaultwarden_data_dir }}/data:/data
environment:
DOMAIN: "https://{{ vaultwarden_domain }}"
SIGNUPS_ALLOWED: "{{ vaultwarden_signups_allowed }}"
ADMIN_TOKEN: "{{ vaultwarden_admin_token }}"
{% if vaultwarden_database_url %}
DATABASE_URL: "{{ vaultwarden_database_url }}"
{% endif %}
ports:
- "127.0.0.1:{{ vaultwarden_port }}:80"

View File

@@ -0,0 +1,27 @@
server {
listen 80;
server_name {{ vaultwarden_domain }};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name {{ vaultwarden_domain }};
ssl_certificate /etc/nginx/ssl/cf-origin.pem;
ssl_certificate_key /etc/nginx/ssl/cf-origin.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Required for Bitwarden web vault
client_max_body_size 525m;
location / {
proxy_pass http://127.0.0.1:{{ vaultwarden_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}