Initial infrastructure setup: Gitea + PostgreSQL + Actions Runner

This commit is contained in:
2026-01-29 18:48:00 +00:00
commit 32fd43ed18
4 changed files with 398 additions and 0 deletions

21
.env.example Normal file
View File

@@ -0,0 +1,21 @@
# Gitea Infrastructure Environment Variables
# Copy to .env and fill in values
# Domain
GITEA_DOMAIN=git.infra.donovankelly.xyz
# PostgreSQL
POSTGRES_USER=gitea
POSTGRES_PASSWORD=CHANGE_ME_generate_with_openssl_rand_hex_16
POSTGRES_DB=gitea
# Gitea Secrets (preserve from old instance's app.ini)
INTERNAL_TOKEN=
LFS_JWT_SECRET=
OAUTH2_JWT_SECRET=
SECRET_KEY=
# Gitea Actions Runner
# Generate after Gitea is running:
# docker exec gitea gitea actions generate-runner-token
RUNNER_REGISTRATION_TOKEN=REPLACE_ME_AFTER_FIRST_DEPLOY

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

249
MIGRATION.md Normal file
View File

@@ -0,0 +1,249 @@
# Gitea Migration: SQLite → PostgreSQL
## Overview
Migrating Gitea from:
- **Old**: `git.infra.nkode.tech` (SQLite, Dokploy on 191.101.0.153)
- **New**: `git.infra.donovankelly.xyz` (PostgreSQL, Dokploy on 191.101.0.153)
This is a **destructive migration** — back up everything before proceeding.
---
## Prerequisites
- [ ] DNS: Point `git.infra.donovankelly.xyz``191.101.0.153`
- [ ] Old Gitea export at `/home/clawdbot/clawd/gitea-export/` on the VPS
- [ ] Docker and docker-compose available on the Dokploy server
- [ ] The new compose service deployed via Dokploy (but NOT yet migrated)
---
## Step-by-Step Migration
### Step 1: DNS Setup
Add an A record:
```
git.infra.donovankelly.xyz → 191.101.0.153
```
Wait for propagation (check with `dig git.infra.donovankelly.xyz`).
### Step 2: Stop the Old Gitea Instance
In Dokploy, stop the old Gitea application (`nkode-giteasqlite`).
```bash
# Or via CLI on 191.101.0.153:
docker stop <old-gitea-container>
```
### Step 3: Deploy the New Stack (Fresh, No Data Yet)
Deploy the compose service from Dokploy. This will start:
- PostgreSQL 16 (empty database)
- Gitea (fresh install, connected to Postgres)
- Act Runner (will fail until token is set — that's OK)
Wait for Gitea to be healthy:
```bash
docker logs -f gitea
# Look for "Starting new Web server"
```
### Step 4: Stop Gitea (Keep Postgres Running)
```bash
docker stop gitea
```
We need Gitea stopped to safely import data.
### Step 5: Migrate the SQLite Database to PostgreSQL
**Option A: Using `gitea dump` and restore (Recommended)**
The export already has `gitea-dump.zip`. We'll use Gitea's built-in migration:
```bash
# Copy the SQLite database into the Gitea container's volume
docker cp /home/clawdbot/clawd/gitea-export/gitea.db gitea:/data/gitea/gitea.db
# Start gitea temporarily to run the migration
docker start gitea
# Run the database migration from SQLite to Postgres
docker exec -u git gitea gitea doctor convert --type sqlite --database /data/gitea/gitea.db
# If `doctor convert` isn't available in your version, use dump/restore:
# First, generate a SQL dump from SQLite using the gitea binary:
docker exec -u git gitea gitea dump --type postgres --database /data/gitea/gitea.db --file /tmp/gitea-pg-dump.sql
# Then import it into Postgres:
docker exec -i gitea-postgres psql -U gitea -d gitea < /tmp/gitea-pg-dump.sql
```
**Option B: Using `gitea migrate` on fresh start**
If the above doesn't work cleanly, use pgloader to convert SQLite → Postgres directly:
```bash
# Install pgloader (on the host or in a container)
docker run --rm --network gitea-net \
-v /home/clawdbot/clawd/gitea-export/gitea.db:/data/gitea.db \
dimitri/pgloader:latest \
pgloader /data/gitea.db \
postgresql://gitea:YOUR_POSTGRES_PASSWORD@postgres:5432/gitea
```
**Option C: Manual approach using Gitea's dump file**
```bash
# Extract the dump
cd /tmp
unzip /home/clawdbot/clawd/gitea-export/gitea-dump.zip
# The dump contains gitea-db.sql (SQLite format)
# Convert using pgloader or sqlite3 + sed for Postgres compatibility
```
**Recommended approach: Option A with `doctor convert`**
### Step 6: Copy Repository Data and LFS
The git repositories, LFS objects, avatars, and other data files need to be copied into the new volume:
```bash
# Find the new gitea-data volume mount point
VOLUME_PATH=$(docker volume inspect gitea-data --format '{{ .Mountpoint }}')
# Stop gitea first
docker stop gitea
# Copy the data directory contents
sudo cp -a /home/clawdbot/clawd/gitea-export/data/git/* ${VOLUME_PATH}/git/
sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/avatars ${VOLUME_PATH}/gitea/
sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/repo-avatars ${VOLUME_PATH}/gitea/
sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/attachments ${VOLUME_PATH}/gitea/
sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/uploads ${VOLUME_PATH}/gitea/ 2>/dev/null || true
sudo cp -a /home/clawdbot/clawd/gitea-export/data/ssh/* ${VOLUME_PATH}/ssh/ 2>/dev/null || true
# Fix ownership (Gitea runs as UID 1000 / git user)
sudo chown -R 1000:1000 ${VOLUME_PATH}/
# Start gitea again
docker start gitea
```
### Step 7: Verify the Migration
```bash
# Check Gitea logs for errors
docker logs gitea --tail 100
# Test the web UI
curl -s https://git.infra.donovankelly.xyz/api/v1/version
# Log in with your existing credentials
# Check that repos, users, orgs, webhooks are all present
```
**Checklist:**
- [ ] Can log in with existing user accounts
- [ ] All repositories are visible and cloneable
- [ ] LFS objects are accessible
- [ ] Webhooks are configured
- [ ] Organizations and teams are intact
- [ ] Avatars display correctly
- [ ] SSH keys work (`ssh -T git@git.infra.donovankelly.xyz -p 22222`)
### Step 8: Set Up Gitea Actions Runner
```bash
# Generate a runner registration token
docker exec -u git gitea gitea actions generate-runner-token
# Copy the token output, then update .env:
# RUNNER_REGISTRATION_TOKEN=<paste-token-here>
# Restart the runner
docker restart gitea-act-runner
# Verify the runner is connected
docker logs gitea-act-runner
```
You can also register the runner via the Gitea web UI:
1. Go to **Site Administration****Runners**
2. Click **Create new runner**
3. Copy the token
### Step 9: Configure Dokploy Domain / SSL
In Dokploy, configure the compose service's domain:
1. Go to the "Infrastructure" project → compose service
2. Add domain `git.infra.donovankelly.xyz` pointing to the gitea service on port 3000
3. Enable HTTPS (Let's Encrypt)
### Step 10: Update Git Remotes
On all machines that had the old remote:
```bash
git remote set-url origin https://git.infra.donovankelly.xyz/<user>/<repo>.git
# or for SSH:
git remote set-url origin ssh://git@git.infra.donovankelly.xyz:22222/<user>/<repo>.git
```
### Step 11: Clean Up
- [ ] Remove the old Gitea application from Dokploy
- [ ] Remove old Docker volumes
- [ ] Update any CI/CD webhooks pointing to the old domain
- [ ] Update any OAuth apps using the old domain
- [ ] Remove DNS record for `git.infra.nkode.tech` (optional, keep for redirect)
---
## Rollback Plan
If migration fails:
1. Stop the new stack: `docker compose down`
2. Restart the old Gitea container in Dokploy
3. The old data is untouched in `/home/clawdbot/clawd/gitea-export/`
---
## Architecture
```
┌─────────────────────────────────────┐
│ Dokploy (Traefik) │
│ git.infra.donovankelly.xyz:443 │
│ ↓ :3000 │
│ ┌─────────────────────────────┐ │
│ │ Gitea 1.24 │ │
│ │ (Actions enabled) │ │
│ │ SSH :222 → external :22222 │ │
│ └──────────┬──────────────────┘ │
│ │ │
│ ┌──────────▼──────────────────┐ │
│ │ PostgreSQL 16 │ │
│ │ (gitea database) │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Act Runner │ │
│ │ (CI/CD workflows) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
```
## Files
| File | Purpose |
|------|---------|
| `docker-compose.yml` | Service definitions |
| `.env` | Secret values (DO NOT commit) |
| `.env.example` | Template for secrets |
| `MIGRATION.md` | This document |

127
docker-compose.yml Normal file
View File

@@ -0,0 +1,127 @@
version: "3.8"
services:
gitea:
image: docker.gitea.com/gitea:1.24
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA_CUSTOM=/data/gitea
# Database
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres:5432
- GITEA__database__NAME=${POSTGRES_DB}
- GITEA__database__USER=${POSTGRES_USER}
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
- GITEA__database__SSL_MODE=disable
# Server
- GITEA__server__DOMAIN=${GITEA_DOMAIN}
- GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN}
- GITEA__server__ROOT_URL=https://${GITEA_DOMAIN}/
- GITEA__server__HTTP_PORT=3000
- GITEA__server__SSH_PORT=22222
- GITEA__server__SSH_LISTEN_PORT=222
- GITEA__server__START_SSH_SERVER=true
- GITEA__server__DISABLE_SSH=false
- GITEA__server__LFS_START_SERVER=true
- GITEA__server__LFS_JWT_SECRET=${LFS_JWT_SECRET}
- GITEA__server__OFFLINE_MODE=true
- GITEA__server__APP_DATA_PATH=/data/gitea
# Security
- GITEA__security__INSTALL_LOCK=true
- GITEA__security__SECRET_KEY=${SECRET_KEY}
- GITEA__security__INTERNAL_TOKEN=${INTERNAL_TOKEN}
- GITEA__security__REVERSE_PROXY_LIMIT=1
- GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES=*
- GITEA__security__PASSWORD_HASH_ALGO=pbkdf2
# Service
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__service__REQUIRE_SIGNIN_VIEW=false
- GITEA__service__ENABLE_OPENID_SIGNUP=false
- GITEA__service__ENABLE_OPENID_SIGNIN=false
# OAuth2
- GITEA__oauth2__JWT_SECRET=${OAUTH2_JWT_SECRET}
# Actions
- GITEA__actions__ENABLED=true
# Repository
- GITEA__repository__ROOT=/data/git/repositories
- GITEA__repository__ENABLE_GO_GET=true
# LFS
- GITEA__lfs__PATH=/data/git/lfs
# Session
- GITEA__session__PROVIDER=file
- GITEA__session__PROVIDER_CONFIG=/data/gitea/sessions
# Log
- GITEA__log__MODE=console
- GITEA__log__LEVEL=info
# Mailer
- GITEA__mailer__ENABLED=false
# Cron
- GITEA__cron.update_checker__ENABLED=false
restart: unless-stopped
volumes:
- gitea-data:/data
ports:
- "3000:3000"
- "22222:222"
depends_on:
postgres:
condition: service_healthy
networks:
- gitea-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/version"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
postgres:
image: postgres:16-alpine
container_name: gitea-postgres
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- gitea-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
act-runner:
image: gitea/act_runner:latest
container_name: gitea-act-runner
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_REGISTRATION_TOKEN}
- GITEA_RUNNER_NAME=default-runner
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://ubuntu:22.04
restart: unless-stopped
volumes:
- runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
gitea:
condition: service_healthy
networks:
- gitea-net
volumes:
gitea-data:
driver: local
postgres-data:
driver: local
runner-data:
driver: local
networks:
gitea-net:
driver: bridge