feat: add Ampache music streaming server to media stack
- Add ampache service to media/compose.yaml with PostgreSQL backend - Create ampache database in postgres init script - Configure Ampache environment variables in arty.yml - Mount Music directory read-only - Expose at ampache.media.pivoine.art via Traefik
This commit is contained in:
130
AGENTS.md
Normal file
130
AGENTS.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
This is a multi-service Docker Compose infrastructure project organized as follows:
|
||||
|
||||
- **Root**: `compose.yaml` (orchestrates all services), `arty.yml` (centralized configuration)
|
||||
- **Service Directories**: Each service has its own folder (`core/`, `sexy/`, `proxy/`, `ai/`, etc.) containing:
|
||||
- `compose.yaml`: Service-specific Docker Compose configuration
|
||||
- Service-specific configuration files and initialization scripts
|
||||
- **Shared Infrastructure**: `core/` provides PostgreSQL 16 and Redis 7 for all services
|
||||
- **Proxy**: `proxy/` contains Traefik reverse proxy with dynamic security configurations in `proxy/dynamic/`
|
||||
|
||||
All services connect to a single external Docker network (`falcon_network`) defined by `$NETWORK_NAME`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
All commands use `pnpm arty` (leveraging scripts defined in `arty.yml`):
|
||||
|
||||
- `pnpm arty up` - Start all services in detached mode
|
||||
- `pnpm arty down` - Stop and remove all containers
|
||||
- `pnpm arty ps` - List running containers
|
||||
- `pnpm arty logs` - Follow logs for all services
|
||||
- `pnpm arty restart` - Restart all services
|
||||
- `pnpm arty pull` - Pull latest images for all services
|
||||
- `pnpm arty config` - View rendered configuration with variables substituted
|
||||
- `pnpm arty net/create` - Create the external Docker network (required before first run)
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
**Environment Variables:**
|
||||
- Pattern: `{SERVICE}_COMPOSE_PROJECT_NAME`, `{SERVICE}_TRAEFIK_HOST`, `{SERVICE}_DOCKER_IMAGE`
|
||||
- Defined in `arty.yml` under `envs.default` with sensible defaults
|
||||
- Sensitive values stored in `.env` (passwords, secrets, API keys)
|
||||
|
||||
**Service Configuration:**
|
||||
- Use `${VARIABLE:-default_value}` syntax for environment variable references
|
||||
- Container names: `${SERVICE_COMPOSE_PROJECT_NAME}_app` or `${SERVICE_COMPOSE_PROJECT_NAME}_component`
|
||||
- Volume names: `${SERVICE_COMPOSE_PROJECT_NAME}_volume_name`
|
||||
|
||||
**Traefik Labels:**
|
||||
- HTTP → HTTPS redirect on `web` entrypoint (port 80)
|
||||
- SSL termination on `web-secure` entrypoint (port 443)
|
||||
- All routers scoped to `${NETWORK_NAME}` network
|
||||
- Use middleware pattern: `${SERVICE_COMPOSE_PROJECT_NAME}-middleware-name`
|
||||
|
||||
**File Organization:**
|
||||
- Database initialization scripts: `core/postgres/init/*.sh`
|
||||
- Dynamic Traefik configuration: `proxy/dynamic/*.yaml`
|
||||
- Service data exports: `{service}/` (e.g., `sexy/directus.yaml`)
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
This is an infrastructure project with no automated test suite. Validation is done through:
|
||||
|
||||
**Service Health Checks:**
|
||||
- Most services include healthcheck definitions in their `compose.yaml`
|
||||
- Check service health: `docker ps` (look for "healthy" status)
|
||||
- Inspect specific service: `docker inspect <container_name>`
|
||||
|
||||
**Manual Verification:**
|
||||
- Access services via configured hostnames (e.g., `https://sexy.pivoine.art`)
|
||||
- Check Traefik dashboard: `https://proxy.pivoine.art` (requires HTTP Basic Auth)
|
||||
- Test SSL certificates: `curl -I https://<service>.pivoine.art`
|
||||
- Monitor logs: `pnpm arty logs` or `docker logs <container_name>`
|
||||
|
||||
**Troubleshooting:**
|
||||
- Database connectivity: `docker exec core_postgres psql -U $DB_USER -l`
|
||||
- Network exists: `docker network ls | grep falcon`
|
||||
- Check configuration rendering: `pnpm arty config`
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
**Commit Message Format:**
|
||||
- Follow conventional commits: `type: description`
|
||||
- Types: `feat`, `fix`, `docs`, `refactor`, `chore`
|
||||
- Keep description concise and imperative mood
|
||||
- Add detailed explanation in commit body when needed
|
||||
- Include co-author attribution: `Co-Authored-By: Claude <noreply@anthropic.com>`
|
||||
- Add generation marker: `🤖 Generated with [Claude Code](https://claude.com/claude-code)`
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
feat: add sexy/bundle/update script to refresh Directus extensions
|
||||
|
||||
Added arty script to update Directus extension bundle from the latest
|
||||
sexy_frontend image. This ensures the API container always has the
|
||||
latest extensions when the frontend image is rebuilt.
|
||||
```
|
||||
|
||||
```
|
||||
fix: enable email functionality in Linkwarden with correct SMTP configuration
|
||||
|
||||
- Add NEXT_PUBLIC_EMAIL_PROVIDER=true to enable email features
|
||||
- Change EMAIL_SERVER protocol from smtp:// to smtps:// for port 465
|
||||
```
|
||||
|
||||
**Changes to Make:**
|
||||
- Update `arty.yml` for new environment variables or scripts
|
||||
- Update service `compose.yaml` files for container configuration
|
||||
- Add initialization scripts to `core/postgres/init/` for new databases
|
||||
- Document new services in README.md
|
||||
- Never commit secrets - they belong in `.env` only
|
||||
|
||||
## Security & Configuration Tips
|
||||
|
||||
**Secrets Management:**
|
||||
- Store all secrets in `.env` (never committed to git)
|
||||
- Use Apache htpasswd format for HTTP Basic Auth: `openssl passwd -apr1 'password'`
|
||||
- Escape `$` signs with `$$` in `.env` files for Docker Compose compatibility
|
||||
|
||||
**Security Headers:**
|
||||
- Global security settings: `proxy/dynamic/security.yaml`
|
||||
- Automatically reloaded by Traefik (no restart needed)
|
||||
- Includes HSTS, X-Frame-Options, CSP, and rate limiting
|
||||
|
||||
**Database Access:**
|
||||
- PostgreSQL exposed on port 5432 for local development
|
||||
- All databases use shared credentials: `$DB_USER` and `$DB_PASSWORD`
|
||||
- Database initialization: `core/postgres/init/01-init-databases.sh`
|
||||
|
||||
**SSL Certificates:**
|
||||
- Automatic Let's Encrypt via Traefik ACME
|
||||
- Requires `ADMIN_EMAIL` in `.env`
|
||||
- Certificates stored in `proxy_letsencrypt_data` volume
|
||||
|
||||
**Network Architecture:**
|
||||
- All services must connect to external network: `${NETWORK_NAME}`
|
||||
- Create network before first run: `pnpm arty net/create`
|
||||
- Network must be external and created separately from compose stacks
|
||||
3
arty.yml
3
arty.yml
@@ -161,6 +161,9 @@ envs:
|
||||
MEDIA_KOEL_DEBUG: false
|
||||
MEDIA_KOEL_MEMORY_LIMIT: 512
|
||||
MEDIA_KOEL_STREAMING_METHOD: x-sendfile
|
||||
MEDIA_AMPACHE_IMAGE: ampache/ampache:latest
|
||||
MEDIA_AMPACHE_TRAEFIK_HOST: ampache.media.pivoine.art
|
||||
MEDIA_AMPACHE_DB_NAME: ampache
|
||||
# PairDrop
|
||||
DROP_TRAEFIK_ENABLED: true
|
||||
DROP_COMPOSE_PROJECT_NAME: drop
|
||||
|
||||
@@ -45,6 +45,10 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
|
||||
SELECT 'CREATE DATABASE koel'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'koel')\\gexec
|
||||
|
||||
-- Ampache music streaming database
|
||||
SELECT 'CREATE DATABASE ampache'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'ampache')\\gexec
|
||||
|
||||
-- Grant privileges to all databases
|
||||
GRANT ALL PRIVILEGES ON DATABASE directus TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE umami TO $POSTGRES_USER;
|
||||
@@ -55,11 +59,12 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
|
||||
GRANT ALL PRIVILEGES ON DATABASE tandoor TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE asciinema TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE koel TO $POSTGRES_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE ampache TO $POSTGRES_USER;
|
||||
|
||||
-- Log success
|
||||
SELECT 'Compose databases initialized:' AS status;
|
||||
SELECT datname FROM pg_database
|
||||
WHERE datname IN ('directus', 'umami', 'n8n', 'linkwarden', 'joplin', 'mattermost', 'tandoor', 'asciinema', 'koel')
|
||||
WHERE datname IN ('directus', 'umami', 'n8n', 'linkwarden', 'joplin', 'mattermost', 'tandoor', 'asciinema', 'koel', 'ampache')
|
||||
ORDER BY datname;
|
||||
EOSQL
|
||||
|
||||
@@ -77,4 +82,5 @@ echo " • mattermost - Chat platform database"
|
||||
echo " • tandoor - Recipe manager database"
|
||||
echo " • asciinema - Terminal recording server database"
|
||||
echo " • koel - Music streaming server database"
|
||||
echo " • ampache - Music streaming server database"
|
||||
echo ""
|
||||
|
||||
@@ -149,6 +149,44 @@ services:
|
||||
networks:
|
||||
- compose_network
|
||||
|
||||
# Ampache - Music streaming server with public sharing
|
||||
ampache:
|
||||
image: ${MEDIA_AMPACHE_IMAGE:-ampache/ampache:latest}
|
||||
container_name: ${MEDIA_COMPOSE_PROJECT_NAME}_ampache
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ampache_config:/var/www/config
|
||||
- ampache_log:/var/log/ampache
|
||||
- /mnt/hidrive/users/valknar/Music:/media:ro
|
||||
environment:
|
||||
TZ: ${TIMEZONE:-Europe/Berlin}
|
||||
DB_HOST: ${CORE_DB_HOST}
|
||||
DB_PORT: ${CORE_DB_PORT}
|
||||
DB_NAME: ${MEDIA_AMPACHE_DB_NAME}
|
||||
DB_USERNAME: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
AMPACHE_WEB_PATH: https://${MEDIA_AMPACHE_TRAEFIK_HOST}
|
||||
networks:
|
||||
- compose_network
|
||||
labels:
|
||||
- 'traefik.enable=${MEDIA_TRAEFIK_ENABLED}'
|
||||
# HTTP to HTTPS redirect
|
||||
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-redirect-web-secure.redirectscheme.scheme=https'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-ampache-redirect-web-secure'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web.rule=Host(`${MEDIA_AMPACHE_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web.entrypoints=web'
|
||||
# HTTPS router
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure.rule=Host(`${MEDIA_AMPACHE_TRAEFIK_HOST}`)'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure.tls.certresolver=resolver'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.middlewares.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure-compress.compress=true'
|
||||
- 'traefik.http.routers.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure.middlewares=${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure-compress,security-headers@file'
|
||||
# Service
|
||||
- 'traefik.http.services.${MEDIA_COMPOSE_PROJECT_NAME}-ampache-web-secure.loadbalancer.server.port=80'
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
# Watchtower
|
||||
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
|
||||
|
||||
volumes:
|
||||
jellyfin_config:
|
||||
name: ${MEDIA_COMPOSE_PROJECT_NAME}_jellyfin_config
|
||||
@@ -160,6 +198,10 @@ volumes:
|
||||
name: ${MEDIA_COMPOSE_PROJECT_NAME}_koel_covers
|
||||
koel_search_index:
|
||||
name: ${MEDIA_COMPOSE_PROJECT_NAME}_koel_search_index
|
||||
ampache_config:
|
||||
name: ${MEDIA_COMPOSE_PROJECT_NAME}_ampache_config
|
||||
ampache_log:
|
||||
name: ${MEDIA_COMPOSE_PROJECT_NAME}_ampache_log
|
||||
|
||||
networks:
|
||||
compose_network:
|
||||
|
||||
Reference in New Issue
Block a user