server_init/resources/compose.yml.j2
Nelis Volschenk 25d6bbf284 Replace opencode with muxplex, add nvm, JuiceFS, and Nextcloud
- Remove opencode; install nvm, Claude Code, and pi-coding-agent for aicoder
- Add muxplex as web terminal service behind Pangolin (auth: none, port 8088)
- Add JuiceFS (Docker container with FUSE) backed by S3 + Redis for Nextcloud storage
- Add Nextcloud + MariaDB with JuiceFS mount via depends_on chain
- Add autoheal container to restart unhealthy services (covers stale FUSE mounts)
- Add SSH key for aicoder user, uv for aicoder, cron cleanup script
- Pin images: major for stable projects, minor for Pangolin/Gerbil
- Query JuiceFS S3 credentials and cache size during init

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-25 03:38:29 +00:00

177 lines
4.8 KiB
Django/Jinja

# Image pinning strategy:
# Pangolin/Gerbil: minor-pinned (newer projects, higher risk of breaking changes)
# Everything else: major-pinned (get security patches automatically)
# Run `docker compose pull && docker compose up -d` to update within pinned range
name: pangolin
services:
pangolin:
image: docker.io/fosrl/pangolin:1.17
container_name: pangolin
restart: unless-stopped
volumes:
- ./docker_data/pangolin:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: docker.io/fosrl/gerbil:1.3
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
volumes:
- ./docker_data/pangolin:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "51820:51820/udp"
- "21820:21820/udp"
- "443:443"
- "80:80"
traefik:
image: docker.io/traefik:v3.6
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./docker_data/pangolin/traefik:/etc/traefik:ro
- ./docker_data/pangolin/letsencrypt:/letsencrypt
- ./docker_data/pangolin/traefik/logs:/var/log/traefik
forgejo:
image: codeberg.org/forgejo/forgejo:15
container_name: forgejo
restart: unless-stopped
volumes:
- ./docker_data/forgejo/data:/data
environment:
- USER_UID={{ git_uid }}
- USER_GID={{ git_gid }}
- FORGEJO__service__DISABLE_REGISTRATION=true
- FORGEJO__server__ROOT_URL=https://{{ forgejo_domain }}
- FORGEJO__server__HTTP_PORT=3001
- FORGEJO__server__DISABLE_SSH=false
- FORGEJO__server__START_SSH_SERVER=false
- FORGEJO__server__SSH_PORT=22
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- ./docker_data/nextcloud/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: "5s"
timeout: "3s"
retries: 10
juicefs:
image: juicedata/mount:ce-v1
container_name: juicefs
restart: unless-stopped
depends_on:
redis:
condition: service_healthy
command: >
juicefs mount
--foreground
--cache-dir /var/cache/juicefs
--cache-size {{ juicefs_cache_size | regex_replace('[^0-9]', '') | int * 1024 }}
--buffer-size 300
--prefetch 1
"redis://redis:6379/1"
/mnt/nextcloud
devices:
- /dev/fuse
cap_add:
- SYS_ADMIN
security_opt:
- apparmor:unconfined
volumes:
- ./docker_data/nextcloud/data:/mnt/nextcloud:rshared
- ./docker_data/juicefs/cache:/var/cache/juicefs
healthcheck:
test: ["CMD", "mountpoint", "-q", "/mnt/nextcloud"]
interval: "10s"
timeout: "5s"
retries: 10
nextcloud-db:
image: mariadb:11
container_name: nextcloud-db
restart: unless-stopped
volumes:
- ./docker_data/nextcloud/db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD={{ nextcloud_db_pass }}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD={{ nextcloud_db_pass }}
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: "10s"
timeout: "5s"
retries: 10
nextcloud:
image: nextcloud:33
container_name: nextcloud
restart: unless-stopped
depends_on:
nextcloud-db:
condition: service_healthy
juicefs:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./docker_data/nextcloud/data:/var/www/html:rshared
environment:
- MYSQL_HOST=nextcloud-db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD={{ nextcloud_db_pass }}
- NEXTCLOUD_ADMIN_USER=admin
- NEXTCLOUD_ADMIN_PASSWORD={{ nextcloud_admin_pass }}
- NEXTCLOUD_TRUSTED_DOMAINS={{ nextcloud_domain }}
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST={{ nextcloud_domain }}
- REDIS_HOST=redis
autoheal:
image: willfarrell/autoheal:1
container_name: autoheal
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=30
networks:
default:
driver: bridge
name: pangolin