Automated fix from pre-commit hooks (trailing-whitespace, end-of-file-fixer). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.7 KiB
Deploying Gitea with 1Password Secrets Management and Caddy on Bigbox
Date: February 16, 2026 Author: Infrastructure Team Tags: gitea, 1password, caddy, docker, tailscale, devops
Executive Summary
This article documents the deployment of a production-ready Gitea instance on bigbox server, featuring enterprise-grade security with 1Password secrets management, Caddy reverse proxy with automatic HTTPS, and network isolation via Tailscale.
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ Internet │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ Caddy (bigbox) │ │
│ │ :443/:80 │ │
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ workflows. git.terraphim. *.terraphim. │
│ terraphim. cloud (NEW) cloud │
│ cloud │ (catchall) │
│ ▼ │
│ ┌──────────────┐ │
│ │ Gitea │ │
│ │ :3000 │ │
│ └──────┬───────┘ │
│ │ │
└───────────────────┼─────────────────────────────────────────────┘
│
┌───────────┴───────────┐
│ Tailscale VPN │
│ (100.106.66.0/8) │
└───────────┬───────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
Prometheus S3 API SeaweedFS
:9000 :8333 (Internal)
Key Features
1. Security-First Design
- No hardcoded secrets - All credentials stored in 1Password
- 1Password integration - Secrets injected at runtime via
op inject - Template-based configuration -
.templatefiles in git, resolved configs excluded - Network isolation - Internal services not exposed to internet
2. Multi-Layer Network Security
| Service | Exposure | Access Method |
|---|---|---|
| Gitea Web | Public | HTTPS via Caddy (git.terraphim.cloud) |
| Gitea SSH | Public | Port 222 direct |
| Prometheus | Tailscale only | http://100.106.66.7:9000 |
| S3 API | Tailscale only | http://100.106.66.7:8333 |
| PostgreSQL | Internal only | Docker network |
| SeaweedFS | Internal only | Docker network |
3. Technology Stack
- Gitea 1.22.6 - Git hosting with Actions support
- PostgreSQL 15 - Database
- SeaweedFS - Distributed S3-compatible object storage
- Prometheus - Metrics and monitoring
- Caddy - Reverse proxy with automatic HTTPS
- 1Password CLI - Secrets management
- Tailscale - VPN for internal services
Deployment Process
Prerequisites
- SSH access to bigbox
- 1Password service account configured (
~/op_zesticai_non_prod.sh) - Caddy already running on bigbox
- Docker and Docker Compose installed
Step 1: Create 1Password Vault Items
eval $(op signin)
# Create PostgreSQL credentials
op item create \
--vault="TerraphimPlatform" \
--category=login \
--title="gitea-postgres" \
--generate-password=20,letters,digits \
username=gitea
# Create S3 credentials
op item create \
--vault="TerraphimPlatform" \
--category=custom \
--title="gitea-s3" \
access-key="$(openssl rand -hex 20)" \
secret-key="$(openssl rand -hex 40)"
Step 2: Template Configuration Files
Create .template files with 1Password references:
docker-compose.yml.template:
version: "3"
services:
server:
image: gitea/gitea:1.22.6
environment:
- GITEA__database__PASSWD=op://TerraphimPlatform/gitea-postgres/password
- GITEA__storage__MINIO_ACCESS_KEY_ID=op://TerraphimPlatform/gitea-s3/access-key
- GITEA__storage__MINIO_SECRET_ACCESS_KEY=op://TerraphimPlatform/gitea-s3/secret-key
# ... other config
s3_config.json.template:
{
"identities": [{
"name": "gitea",
"credentials": [{
"accessKey": "op://TerraphimPlatform/gitea-s3/access-key",
"secretKey": "op://TerraphimPlatform/gitea-s3/secret-key"
}]
}]
}
Step 3: Inject Secrets and Deploy
# Source 1Password credentials
source ~/op_zesticai_non_prod.sh
# Inject secrets into configuration files
op inject --in-file docker-compose.yml.template --out-file docker-compose.yml
op inject --in-file s3_config.json.template --out-file s3_config.json
# Start services
docker compose up -d
Step 4: Configure Caddy
Add Gitea route to existing Caddy instance:
# Add route via Caddy Admin API
curl -X POST http://localhost:2019/config/apps/http/servers/srv0/routes \
-H "Content-Type: application/json" \
-d '{
"@id": "gitea_route",
"match": [{"host": ["git.terraphim.cloud"]}],
"handle": [{
"handler": "subroute",
"routes": [{
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": "localhost:3000"}]
}]
}]
}],
"terminal": true
}'
Important: Route order matters! Add specific routes before wildcard routes.
Network Configuration Details
Port Bindings
Gitea (Public):
127.0.0.1:3000:3000- HTTP via Caddy0.0.0.0:222:22- SSH direct access
Prometheus (Tailscale only):
100.106.66.7:9000:9090- Only accessible via Tailscale
S3 API (Tailscale only):
100.106.66.7:8333:8333- Only accessible via Tailscale100.106.66.7:9327:9327- Prometheus metrics
Docker Compose Network Isolation
networks:
gitea:
external: false # Internal Docker network only
services:
db:
networks:
- gitea # No external ports
master:
# No ports exposed - internal only
volume:
# No ports exposed - internal only
filer:
# No ports exposed - internal only
Security Considerations
1. Secrets Management
- ✅ No secrets in Git repository
- ✅ No secrets in Docker images
- ✅ Secrets injected at runtime
- ✅ Easy rotation via 1Password
2. Network Security
- ✅ Public services behind Caddy with automatic HTTPS
- ✅ Internal services on Tailscale VPN
- ✅ Database isolated in Docker network
- ✅ SSH on non-standard port (222)
3. Access Control
- Prometheus and S3 require Tailscale connection
- Gitea accessible publicly via HTTPS
- SSH access available on port 222
Troubleshooting
Permission Denied Errors
If Gitea shows permission errors:
sudo chown -R 1000:1000 gitea/
sudo chmod -R 755 gitea/
docker compose restart server
Route Not Working
Check Caddy route order:
curl -s http://localhost:2019/config/apps/http/servers/srv0/routes | \
jq '.[] | select(.match[0].host[0] | contains("terraphim")) | .match[0].host[0]'
Specific routes must come before wildcard routes.
1Password Authentication
Verify service account:
source ~/op_zesticai_non_prod.sh
op vault list
Maintenance
Backup
# Backup data directories
rsync -avz bigbox:~/gitea-stack/gitea ./backup/
rsync -avz bigbox:~/gitea-stack/postgres ./backup/
rsync -avz bigbox:~/gitea-stack/seaweedfs ./backup/
Update Images
cd ~/gitea-stack
docker compose pull
docker compose up -d
Rotate Secrets
- Update in 1Password vault
- Re-run deployment:
source ~/op_zesticai_non_prod.sh op inject --in-file docker-compose.yml.template --out-file docker-compose.yml docker compose up -d
Access Points
Public URLs
- Gitea Web: https://git.terraphim.cloud
- Gitea SSH: ssh://git@bigbox:222
Tailscale-only URLs
Connect to Tailscale first: tailscale up
- Prometheus: http://100.106.66.7:9000
- S3 API: http://100.106.66.7:8333
Conclusion
This deployment demonstrates production-ready practices:
- Zero hardcoded secrets
- Defense in depth with network isolation
- Automatic HTTPS via Caddy
- Easy maintenance with Docker Compose
- Centralized secrets management with 1Password
The architecture balances security (Tailscale isolation, 1Password integration) with usability (public HTTPS access to Gitea, direct SSH access).
References
Repository: [Private terraphim repo with configuration]