fahrengit-451/docker-compose.yml
Albert Armea cf99cd50f2 Initial commit
Change written by Claude Sonnet 4.6:

I need to set up a Git hosting service for personal projects where certain repositories need to be blocked using the locale corresponding to the user’s IP address.

Here are my constraints:
*   The entire system will be running on a single VPS instance. I should be able to `docker compose up -d` the whole thing.
*   For each repository that this feature is enabled, I need to be able to set the blocked locales down to the state level, along with a custom HTTP status code and response body.
*   You may assume that the IP address of the request is where it actually came from — for this exercise, if the user uses a VPN to bypass the restriction, that is on them.
*   To simplify a reverse proxy setup, you may assume that all Git operations will happen over HTTPS. I will firewall off SSH access.
*    I will be using Let's Encrypt for HTTPS.

Some suggestions from prior research:
*   nginx seems like a reasonable reverse proxy that supports all of the requirements, but you may use a different one if it is simpler to implement or maintain.
*   I can obtain a MaxMind API key to get a geo-IP lookup table. If you use this, you will need to add a service that automatically retrieves the table at a reasonable frequency.
*   Forgejo seems like a reasonable, lightweight Git service, but you may use a different one if you’re aware of one that actually supports these requirements out of the box.

Write me a production-ready `docker-compose.yml` and supporting scripts or configuration scaffolding for me to implement this.
2026-03-21 18:34:50 +00:00

123 lines
4.1 KiB
YAML

services:
# ── Forgejo ────────────────────────────────────────────────────────────────
forgejo:
image: codeberg.org/forgejo/forgejo:9
container_name: forgejo
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__server__DOMAIN=${DOMAIN}
- FORGEJO__server__ROOT_URL=https://${DOMAIN}/
- FORGEJO__server__HTTP_PORT=3000
- FORGEJO__server__DISABLE_SSH=true
- FORGEJO__service__DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-true}
- FORGEJO__database__DB_TYPE=sqlite3
- FORGEJO__database__PATH=/data/forgejo/forgejo.db
- FORGEJO__log__LEVEL=Info
volumes:
- forgejo_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- internal
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/healthz"]
interval: 30s
timeout: 5s
retries: 3
# ── nginx (reverse proxy + GeoIP blocking) ─────────────────────────────────
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro # static config fragments
- ./nginx/geoblock:/etc/nginx/geoblock:ro # rendered map snippet (written by watcher)
- ./certs/live:/etc/letsencrypt/live:ro
- ./certs/archive:/etc/letsencrypt/archive:ro
- ./certs/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf:ro
- ./certs/ssl-dhparams.pem:/etc/letsencrypt/ssl-dhparams.pem:ro
- certbot_webroot:/var/www/certbot:ro
- geoip_db:/usr/share/GeoIP:ro
- nginx_logs:/var/log/nginx
networks:
- internal
depends_on:
- forgejo
environment:
- DOMAIN=${DOMAIN}
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 60s
timeout: 5s
retries: 3
# ── MaxMind GeoIP database updater ────────────────────────────────────────
geoipupdate:
image: ghcr.io/maxmind/geoipupdate:v7
container_name: geoipupdate
restart: unless-stopped
environment:
- GEOIPUPDATE_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID}
- GEOIPUPDATE_LICENSE_KEY=${MAXMIND_LICENSE_KEY}
- GEOIPUPDATE_EDITION_IDS=GeoLite2-City
- GEOIPUPDATE_FREQUENCY=72 # hours — MaxMind updates twice a week
- GEOIPUPDATE_DB_DIR=/usr/share/GeoIP
volumes:
- geoip_db:/usr/share/GeoIP
networks:
- internal
# ── Geo-block config watcher ───────────────────────────────────────────────
# Watches geo_rules.yml; re-renders the nginx map snippet and reloads nginx
# whenever rules change.
geoblock_watcher:
build:
context: ./geoblock_watcher
dockerfile: Dockerfile
container_name: geoblock_watcher
restart: unless-stopped
volumes:
- ./geo_rules.yml:/app/geo_rules.yml:ro
- ./nginx/geoblock:/app/geoblock # shared with nginx (rw here)
- /var/run/docker.sock:/var/run/docker.sock
networks:
- internal
depends_on:
- nginx
# ── Certbot (Let's Encrypt) ────────────────────────────────────────────────
certbot:
image: certbot/certbot:latest
container_name: certbot
restart: unless-stopped
volumes:
- ./certs:/etc/letsencrypt
- certbot_webroot:/var/www/certbot
entrypoint: >
/bin/sh -c "
trap exit TERM;
while :; do
certbot renew --webroot -w /var/www/certbot --quiet;
sleep 12h &
wait $${!};
done
"
volumes:
forgejo_data:
geoip_db:
certbot_webroot:
nginx_logs:
networks:
internal:
driver: bridge