This template allows deploying a forgejo en either Scaleway or Hetzner (untested) without much knowledge about them. It DOES require knowledge about Terragrunt and ansible. A wizard of sorts is provided but it will not guarantee success without some knowledge about the underlying technology.
14 KiB
Configuration Reference
This document explains all configuration options for the Forgejo self-hosting setup.
Table of Contents
- Domain Configuration
- Secrets (Vault Variables)
- Feature Flags
- Security (Tailscale + UFW)
- S3/Object Storage
- Database Configuration
- Email Configuration
- Monitoring (Prometheus)
- Backup Configuration
Domain Configuration
The domain is configured in one primary location:
File: ansible/inventory/production/hosts.yml
forgejo_domain: git.yourdomain.com
This domain is used for:
- HTTPS certificate (automatically obtained via Caddy/Let's Encrypt)
- Forgejo web interface URL
- Git clone URLs:
https://git.yourdomain.com/user/repo.git - SSH clone URLs:
git@git.yourdomain.com:user/repo.git - Email "From" addresses
Before deployment, ensure your DNS is configured:
git.yourdomain.com. IN A <server-ipv4>
git.yourdomain.com. IN AAAA <server-ipv6>
Secrets (Vault Variables)
All secrets are stored in ansible/playbooks/vars/secrets.yml encrypted with Ansible Vault.
How to Set Up Secrets
# 1. Copy the example file
cp ansible/playbooks/vars/secrets.yml.example ansible/playbooks/vars/secrets.yml
# 2. Edit with your values
nano ansible/playbooks/vars/secrets.yml
# 3. Encrypt it
ansible-vault encrypt ansible/playbooks/vars/secrets.yml
# 4. To edit later
ansible-vault edit ansible/playbooks/vars/secrets.yml
Secret Variables Explained
| Variable | Purpose | How to Generate |
|---|---|---|
vault_forgejo_db_password |
PostgreSQL database password for Forgejo | openssl rand -base64 32 |
vault_forgejo_admin_password |
Initial admin account password | Choose a strong password |
vault_forgejo_secret_key |
Used for CSRF tokens, session cookies, and encryption. Must be 64+ characters. | openssl rand -base64 48 |
vault_forgejo_internal_token |
Used for internal API calls between Forgejo components | openssl rand -base64 48 |
vault_forgejo_jwt_secret |
Signs JWT tokens for OAuth2 and API authentication | openssl rand -base64 32 |
vault_forgejo_metrics_token |
Required to access /metrics endpoint (if Prometheus enabled) |
openssl rand -base64 32 |
vault_email_password |
SMTP password (if email enabled) | Your email provider password |
vault_s3_access_key |
S3-compatible storage access key (if S3 enabled) | From your cloud provider |
vault_s3_secret_key |
S3-compatible storage secret key (if S3 enabled) | From your cloud provider |
Example secrets.yml
---
vault_forgejo_db_password: "xK9mN2pL8qR5tW7vY3zB1cD4fG6hJ0kM"
vault_forgejo_admin_password: "MySecureAdminPassword123!"
vault_forgejo_secret_key: "aB3cD5eF7gH9iJ1kL3mN5oP7qR9sT1uV3wX5yZ7aB9cD1eF3gH5iJ7kL9mN1oP"
vault_forgejo_internal_token: "qW2eR4tY6uI8oP0aS2dF4gH6jK8lZ0xC2vB4nM6qW8eR0tY2uI4oP6aS8dF0g"
vault_forgejo_jwt_secret: "mN3bV5cX7zL9kJ1hG3fD5sA7pO9iU1yT"
vault_forgejo_metrics_token: "pR0mE7hEuS_t0K3n_H3r3"
vault_email_password: ""
vault_s3_access_key: ""
vault_s3_secret_key: ""
Feature Flags
Configure features in ansible/inventory/production/hosts.yml:
Core Features
| Flag | Default | Description |
|---|---|---|
forgejo_enable_letsencrypt |
true |
Automatic HTTPS via Let's Encrypt (handled by Caddy) |
forgejo_enable_lfs |
true |
Git Large File Storage support |
forgejo_enable_2fa |
true |
Allow users to enable Two-Factor Authentication |
forgejo_use_redis |
true |
Use Redis for caching (recommended for performance) |
forgejo_enable_backups |
true |
Enable automated daily backups |
forgejo_enable_prometheus |
false |
Enable internal Prometheus metrics collection |
Access Control
| Flag | Default | Description |
|---|---|---|
forgejo_disable_registration |
false |
Disable public user registration (invite-only) |
forgejo_require_signin_view |
false |
Require login to view public repositories |
Optional Services
| Flag | Default | Description |
|---|---|---|
forgejo_enable_email |
false |
Enable email notifications (requires SMTP config) |
forgejo_enable_s3 |
false |
Use S3-compatible storage for LFS and attachments |
Example Configuration
# In ansible/inventory/production/hosts.yml
forgejo_prod:
# ... other settings ...
# Enable all recommended features
forgejo_enable_letsencrypt: true
forgejo_enable_lfs: true
forgejo_enable_2fa: true
forgejo_use_redis: true
forgejo_enable_backups: true
# Enable monitoring
forgejo_enable_prometheus: true
# Private instance (no public registration)
forgejo_disable_registration: true
forgejo_require_signin_view: false
Security (Tailscale + UFW)
This setup uses Tailscale VPN and UFW firewall to secure your Forgejo instance:
- SSH access: Only via Tailscale (not exposed to the public internet)
- Git SSH (port 2222): Only via Tailscale
- Web interface: Public via HTTPS (ports 80/443)
- Internal services: Only via Tailscale (Prometheus, database, etc.)
Enable Security Features
# In ansible/inventory/production/hosts.yml
forgejo_enable_tailscale: true
forgejo_enable_ufw: true
How It Works
- Tailscale creates a secure mesh VPN network
- UFW is configured to:
- Allow all traffic on the
tailscale0interface - Allow only HTTP/HTTPS (80/443) from the public internet
- Block SSH from the public internet
- Allow all traffic on the
Post-Deployment: Authenticate Tailscale
After deployment, SSH into the server (while SSH is still open) and authenticate Tailscale:
# SSH into server (before UFW locks down SSH)
ssh root@<server-public-ip>
# Authenticate Tailscale
sudo tailscale up --ssh
# This will print a URL - open it in your browser to authenticate
For headless/automated setup, use an auth key:
sudo tailscale up --authkey=tskey-auth-XXXXX
Generate auth keys at: https://login.tailscale.com/admin/settings/keys
Accessing Your Server After Setup
Once UFW is configured, SSH is only accessible via Tailscale:
# Via Tailscale IP
ssh root@100.x.x.x
# Via Tailscale hostname (from admin console)
ssh root@your-server.tailnet-name.ts.net
# Via Tailscale SSH (if enabled with --ssh)
tailscale ssh root@your-server
Git Clone URLs
| Method | URL | Access |
|---|---|---|
| HTTPS | https://git.yourdomain.com/user/repo.git |
Public |
| SSH | git@<tailscale-hostname>:user/repo.git |
Tailscale only |
Firewall Rules Summary
| Port | Protocol | Access | Purpose |
|---|---|---|---|
| 80 | TCP | Public | HTTP (redirects to HTTPS) |
| 443 | TCP | Public | HTTPS (Forgejo web) |
| 22 | TCP | Tailscale only | System SSH |
| 2222 | TCP | Tailscale only | Git SSH |
| 3000 | TCP | Tailscale only | Forgejo internal |
| 9090 | TCP | Tailscale only | Prometheus |
Disabling Security Features
If you need public SSH access (not recommended):
forgejo_enable_tailscale: false
forgejo_enable_ufw: false
Or configure UFW manually after deployment.
S3/Object Storage
S3-compatible object storage can be used for:
- Git LFS - Large file storage
- Backups - Off-site backup storage
- Attachments - Issue/PR attachments (future)
What is S3?
S3 (Simple Storage Service) is an object storage protocol. Both Scaleway and Hetzner offer S3-compatible storage:
- Scaleway: Object Storage (S3-compatible)
- Hetzner: Object Storage (S3-compatible, in beta)
Setting Up S3 Storage
For Scaleway
-
Create storage via Terraform (already included):
make terraform-apply PROVIDER=scaleway -
Get credentials from Terraform output:
cd terraform/scaleway/storage terragrunt output access_key terragrunt output secret_key -
Configure in inventory:
forgejo_enable_s3: true forgejo_s3_endpoint: https://s3.fr-par.scw.cloud forgejo_s3_bucket: your-project-production-lfs forgejo_s3_region: fr-par -
Add credentials to secrets.yml:
vault_s3_access_key: "SCWXXXXXXXXXXXXXXXXX" vault_s3_secret_key: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
For Hetzner
Hetzner Object Storage is S3-compatible:
-
Create a storage box in Hetzner Cloud Console
-
Configure in inventory:
forgejo_enable_s3: true forgejo_s3_endpoint: https://fsn1.your-objectstorage.com forgejo_s3_bucket: forgejo-lfs forgejo_s3_region: fsn1 -
Add credentials to secrets.yml
S3 for Backups
To upload backups to S3:
# In inventory
forgejo_backup_to_s3: true
forgejo_backup_s3_bucket: your-project-production-backups
Then run:
make backup-to-s3
Database Configuration
PostgreSQL is the recommended database.
Settings
| Variable | Default | Description |
|---|---|---|
forgejo_db_type |
postgres |
Database type (postgres recommended) |
forgejo_db_host |
localhost |
Database host |
forgejo_db_port |
5432 |
Database port |
forgejo_db_name |
forgejo |
Database name |
forgejo_db_user |
forgejo |
Database user |
The password is in vault_forgejo_db_password.
PostgreSQL Tuning
Default tuning in ansible/roles/forgejo/defaults/main.yml:
postgres_version: "16"
postgres_max_connections: 100
postgres_shared_buffers: "256MB"
postgres_effective_cache_size: "1GB"
For larger instances, adjust these based on available RAM.
Email Configuration
Enable email for notifications, password resets, and registration confirmation.
Settings
# In inventory
forgejo_enable_email: true
forgejo_email_host: smtp.example.com
forgejo_email_port: 587
forgejo_email_user: noreply@yourdomain.com
# In secrets.yml
vault_email_password: "your-smtp-password"
Common SMTP Providers
Gmail (with App Password):
forgejo_email_host: smtp.gmail.com
forgejo_email_port: 587
forgejo_email_user: your-email@gmail.com
Mailgun:
forgejo_email_host: smtp.mailgun.org
forgejo_email_port: 587
forgejo_email_user: postmaster@your-domain.mailgun.org
SendGrid:
forgejo_email_host: smtp.sendgrid.net
forgejo_email_port: 587
forgejo_email_user: apikey
# vault_email_password should be your SendGrid API key
Monitoring (Prometheus)
Internal Prometheus monitoring for your Forgejo instance.
Enable Monitoring
# In inventory
forgejo_enable_prometheus: true
What Gets Monitored
- Forgejo metrics: HTTP requests, Git operations, users, repos, issues
- Prometheus self-monitoring: Scrape health
Accessing Metrics
Prometheus is internal only (bound to localhost:9090). To access:
-
SSH tunnel:
ssh -L 9090:localhost:9090 root@your-serverThen open http://localhost:9090
-
Forgejo metrics endpoint:
https://git.yourdomain.com/metrics?token=YOUR_METRICS_TOKENThe token is
vault_forgejo_metrics_token.
Adding Grafana (Optional)
To add Grafana dashboards, extend the monitoring setup:
# Create docker-compose.grafana.yml manually
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "127.0.0.1:3001:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=your-grafana-password
networks:
- forgejo-network
Backup Configuration
Settings
| Variable | Default | Description |
|---|---|---|
forgejo_enable_backups |
true |
Enable automated backups |
forgejo_backup_schedule |
0 2 * * * |
Cron schedule (default: 2 AM daily) |
forgejo_backup_retention_days |
30 |
Days to keep local backups |
forgejo_backup_to_s3 |
false |
Upload backups to S3 |
forgejo_backup_s3_bucket |
"" |
S3 bucket for backups |
What Gets Backed Up
- PostgreSQL database - Full SQL dump
- Git repositories - All repository data
- Configuration - app.ini, docker-compose.yml
- User data - Attachments, LFS files, avatars
Backup Commands
# Manual backup
make backup
# Backup and upload to S3
make backup-to-s3
# Restore from backup
make restore
# You'll be prompted for the backup timestamp
Backup Location
Local backups are stored in: /opt/forgejo/backups/
Files:
database-TIMESTAMP.sql.gzrepositories-TIMESTAMP.tar.gzconfig-TIMESTAMP.tar.gzdata-TIMESTAMP.tar.gz
Quick Reference: Enable Everything
For a fully-featured setup with all options enabled:
# ansible/inventory/production/hosts.yml
forgejo-prod:
ansible_host: YOUR_SERVER_IP
ansible_user: root
# Domain
forgejo_domain: git.yourdomain.com
# Core
forgejo_version: "9.0.2"
forgejo_enable_letsencrypt: true
forgejo_enable_lfs: true
forgejo_enable_2fa: true
forgejo_use_redis: true
# Database
forgejo_db_type: postgres
# Backups
forgejo_enable_backups: true
forgejo_backup_retention_days: 30
forgejo_backup_to_s3: true
forgejo_backup_s3_bucket: your-backup-bucket
# S3 Storage
forgejo_enable_s3: true
forgejo_s3_endpoint: https://s3.fr-par.scw.cloud
forgejo_s3_bucket: your-lfs-bucket
forgejo_s3_region: fr-par
# Email
forgejo_enable_email: true
forgejo_email_host: smtp.mailgun.org
forgejo_email_port: 587
forgejo_email_user: noreply@yourdomain.com
# Monitoring
forgejo_enable_prometheus: true
# Access
forgejo_disable_registration: false # Set to true for invite-only
forgejo_require_signin_view: false
Then in ansible/playbooks/vars/secrets.yml:
vault_forgejo_db_password: "GENERATED_PASSWORD"
vault_forgejo_admin_password: "YOUR_ADMIN_PASSWORD"
vault_forgejo_secret_key: "64_CHAR_GENERATED_KEY"
vault_forgejo_internal_token: "GENERATED_TOKEN"
vault_forgejo_jwt_secret: "GENERATED_SECRET"
vault_forgejo_metrics_token: "GENERATED_TOKEN"
vault_email_password: "YOUR_SMTP_PASSWORD"
vault_s3_access_key: "YOUR_S3_ACCESS_KEY"
vault_s3_secret_key: "YOUR_S3_SECRET_KEY"