Lightweight GitHub alternative. Single Go binary, includes git hosting, issues, PRs, CI webhooks, wiki, packages, built-in SSH.
+--------+ HTTPS +--------+ +-------+ |Developer|<===========>| Gitea |<===>|PostgreS| | Git CLI | SSH:2222 | :3000 | | / SQLite +--------+ +--------+ +-------+
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
USER_UID: 1000
USER_GID: 1000
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: gitea-db:5432
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD: changeme
volumes:
- ./gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"
depends_on: [gitea-db]
restart: unless-stopped
gitea-db:
image: postgres:15-alpine
container_name: gitea-db
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD: changeme
POSTGRES_DB: gitea
volumes:
- ./gitea-db:/var/lib/postgresql/data
restart: unless-stopped
git.example.com {
reverse_proxy gitea:3000
}
Dump all repos: gitea dump or backup gitea-data/ and gitea-db/
curl -s http://localhost:3000/api/v1/version
Community-driven Gitea fork with ActivityPub federation, better CI, stronger FOSS focus. Same lightweight footprint.
Developer --> Forgejo (:3000) --> ActivityPub
|
Git Repos
Issues
PRs
Wiki
services:
forgejo:
image: codeberg.org/forgejo/forgejo:latest
container_name: forgejo
environment:
USER_UID: 1000
USER_GID: 1000
volumes:
- ./forgejo-data:/data
ports:
- "3000:3000"
- "2222:22"
restart: unless-stopped
forgejo.example.com {
reverse_proxy forgejo:3000
}
Backup forgejo-data/ directory
curl -s http://localhost:3000/api/v1/version
The web server that makes HTTPS automatic. Single binary, automatic Let's Encrypt, reverse proxy, file server. Write a 3-line Caddyfile and done.
Internet --> Caddy (:80/:443) --> Services
|
Auto HTTPS
Reverse Proxy
Rate Limiting
Compression
services:
caddy:
image: caddy:latest
container_name: caddy
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy-data:/data
- ./caddy-config:/config
restart: unless-stopped
# Caddy IS the reverse proxy. Example Caddyfile:
# app.example.com { reverse_proxy app:8080 }
Backup Caddyfile and caddy-data/ (certs, keys)
curl -sI https://localhost | head -3
Battle-tested reverse proxy, load balancer, static file server, caching. Configure once, forget forever.
Internet --> Nginx (:80/:443) --> Backend Services
|
Load Balancing
Caching
Rate Limiting
services:
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./sites:/etc/nginx/conf.d
- ./certs:/etc/nginx/certs:ro
restart: unless-stopped
# Use Caddy instead for auto-HTTPS. # If using Nginx, manage certs manually with certbot.
Backup nginx.conf, sites/, and certs/
curl -sI http://localhost
Lightweight CI/CD that runs as single binary + Docker agent. YAML pipelines, integrates with Gitea/GitHub.
Git Push --> Woodpecker Server (:8000) --> Agent --> Docker
|
Webhook
Pipeline
Execution
services:
woodpecker-server:
image: woodpeckerci/woodpecker-server:latest
container_name: woodpecker
environment:
WOODPECKER_HOST: https://ci.example.com
WOODPECKER_GITEA: "true"
WOODPECKER_GITEA_URL: http://gitea:3000
WOODPECKER_GITEA_CLIENT: your-client-id
WOODPECKER_GITEA_SECRET: your-secret
WOODPECKER_AGENT_SECRET: changeme
volumes:
- ./woodpecker-data:/var/lib/woodpecker
ports:
- "8000:8000"
restart: unless-stopped
woodpecker-agent:
image: woodpeckerci/woodpecker-agent:latest
container_name: woodpecker-agent
environment:
WOODPECKER_SERVER: woodpecker:9000
WOODPECKER_AGENT_SECRET: changeme
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on: [woodpecker-server]
restart: unless-stopped
ci.example.com {
reverse_proxy woodpecker:8000
}
Backup woodpecker-data/ directory
curl -s http://localhost:8000/api/version
Self-hosted Zapier alternative. Visual workflow builder with 300+ integrations. Connect APIs, webhooks, databases with drag-and-drop.
Trigger (Webhook/Cron/API) --> n8n (:5678) --> Action (HTTP/DB/Email)
|
Workflow Engine
300+ Integrations
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
environment:
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.example.com
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: admin
N8N_BASIC_AUTH_PASSWORD: changeme
volumes:
- ./n8n-data:/home/node/.n8n
ports:
- "5678:5678"
restart: unless-stopped
n8n.example.com {
reverse_proxy n8n:5678
}
Backup n8n-data/ directory (workflows + credentials)
curl -s http://localhost:5678/healthz
Create agents that monitor and act on your behalf. Scrape websites, watch RSS feeds, send notifications. Your own IFTTT.
Web/RSS/API --> Huginn Agent (:3000) --> Action
|
Event Pipeline
Scheduling
Webhooks
services:
huginn:
image: ghcr.io/huginn/huginn:latest
container_name: huginn
environment:
HUGINN_DATABASE_ADAPTER: postgresql
HUGINN_DATABASE_HOST: huginn-db
HUGINN_DATABASE_USERNAME: huginn
HUGINN_DATABASE_PASSWORD: changeme
HUGINN_SEED_DATABASE: "true"
ports:
- "3000:3000"
depends_on: [huginn-db]
restart: unless-stopped
huginn-db:
image: postgres:15-alpine
container_name: huginn-db
environment:
POSTGRES_USER: huginn
POSTGRES_PASSWORD: changeme
POSTGRES_DB: huginn
volumes:
- ./huginn-db:/var/lib/postgresql/data
restart: unless-stopped
huginn.example.com {
reverse_proxy huginn:3000
}
Dump DB: pg_dump huginn > backup.sql
curl -s http://localhost:3000/ | grep -q Huginn
Monitor websites for changes and get notified. Track price drops, news updates. Visual selector, JSON API, screenshot diffs.
URLs --> changedetection (:5000) --> Notifications
|
Diff Engine
Visual Selector
Screenshots
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
volumes:
- ./cd-data:/datastore
ports:
- "5000:5000"
restart: unless-stopped
changedetection.example.com {
reverse_proxy changedetection:5000
}
Backup cd-data/ directory
curl -s http://localhost:5000/ | grep -q changedetection
Real-time performance monitoring with beautiful dashboards. Zero config, auto-detects everything. CPU, RAM, disk, network, Docker, 800+ integrations.
System --> Netdata (:19999) --> Dashboard
|
Metrics Collector
Streaming
Alerts
services:
netdata:
image: netdata/netdata:latest
container_name: netdata
hostname: myserver
volumes:
- ./netdata-config:/etc/netdata
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "19999:19999"
cap_add: [SYS_PTRACE]
restart: unless-stopped
monitor.example.com {
reverse_proxy netdata:19999
}
Config is auto-generated. Backup netdata-config/ for custom settings.
curl -s http://localhost:19999/api/v1/info
Beautiful uptime monitoring. Monitor HTTP(s), TCP, DNS, Docker. Slack/Discord/Telegram notifications. The prettiest status page ever.
Services --> Uptime Kuma (:3001) --> Status Page
|
Ping/HTTP Check
Notifications
90+ Integrations
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
volumes:
- ./uptime-kuma-data:/app/data
ports:
- "3001:3001"
restart: unless-stopped
status.example.com {
reverse_proxy uptime-kuma:3001
}
Backup uptime-kuma-data/ directory (SQLite DB)
curl -s http://localhost:3001/metrics
Real-time web log analyzer. Terminal UI or HTML reports. Analyze nginx/caddy logs instantly.
Nginx/Caddy Logs --> GoAccess --> Terminal UI
| or
Parse/Analyze HTML Report
|
Real-time
services:
goaccess:
image: allinurl/goaccess:latest
container_name: goaccess
volumes:
- /var/log/nginx:/var/log/nginx:ro
- ./goaccess-reports:/srv/report
ports:
- "7890:7890"
command: goaccess /var/log/nginx/access.log -o /srv/report/index.html --log-format=COMBINED --real-time-html
restart: unless-stopped
goaccess.example.com {
reverse_proxy goaccess:7890
}
Generated reports are in goaccess-reports/. Re-run to update.
goaccess /var/log/nginx/access.log --log-format=COMBINED
Client-side encrypted pastebin. Zero knowledge on server. Burn-after-reading, password protection, syntax highlighting.
User --> PrivateBin (:8080) --> Encrypted Storage
|
Client-side
Encryption
Zero Knowledge
services:
privatebin:
image: privatebin/nginx-fpm-alpine:latest
container_name: privatebin
volumes:
- ./privatebin-data:/srv/data
ports:
- "8080:80"
restart: unless-stopped
paste.example.com {
reverse_proxy privatebin:80
}
Backup privatebin-data/ directory (encrypted pastes)
curl -s http://localhost:80/ | grep -q PrivateBin
Encrypted file sharing. Files encrypted client-side before upload. Set expiry dates, download limits, passwords.
User --> Lufi (:8181) --> Encrypted File Storage
|
Client-side
Encryption
Expiration
services:
lufi:
image: lucasgreff/lufi:latest
container_name: lufi
volumes:
- ./lufi-data:/data
ports:
- "8181:8181"
environment:
CONTACT: admin@example.com
MAX_FILE_SIZE: 100000000
restart: unless-stopped
lufi.example.com {
reverse_proxy lufi:8181
}
Backup lufi-data/ directory
curl -sI http://localhost:8181/
Tiny self-contained pastebin and file server in Rust. Single binary, no database, supports URLs, file uploads, raw serving, expiration.
User --> MicroBin (:8080) --> File Storage
|
Pastes
File Uploads
URLs
Raw Serving
services:
microbin:
image: databros/microbin:latest
container_name: microbin
environment:
MICROBIN_ADMIN_USERNAME: admin
MICROBIN_ADMIN_PASSWORD: changeme
MICROBIN_SHOW_READ_URLS: "true"
volumes:
- ./microbin-data:/app/pasta
ports:
- "8080:8080"
restart: unless-stopped
microbin.example.com {
reverse_proxy microbin:8080
}
Backup microbin-data/ directory
curl -s http://localhost:8080/ | grep -q MicroBin
Self-hosted URL shortener with REST API, QR codes, visit stats, geo-tracking, multi-domain support. Your own bit.ly with full analytics.
User --> Shlink (:8080) --> Database | | v Analytics Short URL GeoIP QR Code Stats
services:
shlink:
image: shlinkio/shlink:latest
container_name: shlink
environment:
DEFAULT_DOMAIN: s.example.com
IS_HTTPS_ENABLED: "true"
GEOLITE_LICENSE_KEY: your-key
DB_DRIVER: postgres
DB_HOST: shlink-db
DB_NAME: shlink
DB_USER: shlink
DB_PASSWORD: changeme
ports:
- "8080:8080"
depends_on: [shlink-db]
restart: unless-stopped
shlink-web:
image: shlinkio/shlink-web-client:latest
container_name: shlink-web
ports:
- "8081:80"
restart: unless-stopped
shlink-db:
image: postgres:15-alpine
container_name: shlink-db
environment:
POSTGRES_USER: shlink
POSTGRES_PASSWORD: changeme
POSTGRES_DB: shlink
volumes:
- ./shlink-db:/var/lib/postgresql/data
restart: unless-stopped
links.example.com {
reverse_proxy shlink:8080
}
links-admin.example.com {
reverse_proxy shlink-web:80
}
Dump DB: pg_dump shlink > backup.sql
curl -s http://localhost:8080/rest/v2/health