This guide walks you through every step needed to add a new service to the atl.chat monorepo — from creating the app directory to wiring it into Docker Compose, theDocumentation Index
Fetch the complete documentation index at: https://docs.allthingslinux.org/llms.txt
Use this file to discover all available pages before exploring further.
just task runner, and the documentation site.
Overview
Each service in atl.chat follows a consistent pattern:- Application code and container definition live in
apps/<service>/ - A Docker Compose fragment lives in
infra/compose/<service>.yaml - Environment variables are declared in
.env.example - Config templates use
envsubstfor variable substitution at init time - Persistent data directories are created by
scripts/init.sh - A
justfilemodule exposes per-service commands - Documentation lives in
apps/docs/content/docs/services/<service>/
- Bridge (
apps/bridge/) — Python service with a simple Containerfile and compose fragment - The Lounge (
apps/thelounge/) — Node.js service with user management recipes - UnrealIRCd (
apps/unrealircd/) — Complex multi-stage build with extensive configuration
1. Create the app directory
Createapps/<service>/ with your application code, a Containerfile, and a .dockerignore.
Containerfile
Use a multi-stage build: a build stage that compiles or installs dependencies, and a minimal runtime stage that copies only what is needed. Here is a minimal example modelled after the Bridge service:- Always run as a non-root user in the runtime stage
- Include a
HEALTHCHECKinstruction — composedepends_onconditions rely on it - Use
EXPOSEto document the ports your service listens on - Use build arguments (
ARG) for version pinning (seeapps/unrealircd/Containerfilefor an example)
apps/bridge/Containerfile, apps/unrealircd/Containerfile
.dockerignore
Add a.dockerignore to keep the build context small:
Verify the image builds
2. Create the Docker Compose fragment
Each service gets its own compose fragment atinfra/compose/<service>.yaml. This keeps the root compose.yaml clean — it only uses include: directives.
Write the compose fragment
Createinfra/compose/<service>.yaml following this pattern (modelled after infra/compose/bridge.yaml):
include: - networks.yaml— every fragment includes the shared network definitiondepends_onwithcondition: service_healthy— use this when your service needs another service to be ready (e.g., IRC server, XMPP server)env_file— load both.env(base) and.env.dev(dev overrides) withrequired: false- Bind-mount volumes — atl.chat uses bind-mount
data/directories, not named Docker volumes - Port binding — bind to
${ATL_CHAT_IP:-127.0.0.1}so ports are not exposed on all interfaces by default
infra/compose/bridge.yaml, infra/compose/irc.yaml
Special case: network namespace sharing
If your service needs to share a network namespace with another container (like Atheme shares with UnrealIRCd), usenetwork_mode: service:<other-service> instead of joining the atl-chat network directly. See the atl-irc-services definition in infra/compose/irc.yaml for this pattern.
Register in the root compose.yaml
Add your fragment to theinclude: list in the root compose.yaml:
- Use the
SCREAMING_SNAKE_CASEnaming convention with a service prefix (e.g.,BRIDGE_,THELOUNGE_) - Provide sensible defaults for development
- Mark secrets with a
change_me_prefix so they are obvious in audits - Add a comment describing each variable
Add config templates (if needed)
If your service needs a configuration file that references environment variables, create a template:- Create
apps/<service>/config/<service>.conf.templatewith${VARIABLE}placeholders - Add the
envsubstsubstitution step toscripts/prepare-config.sh
prepare-config.sh:
scripts/prepare-config.sh — see how UnrealIRCd, Atheme, Bridge, and The Lounge configs are templated.
Document new variables
Add all new variables to the Environment Variables reference. Include the variable name, description, whether it is required, and its default value. Flag any secrets with a security warning.4. Set up persistent data directories
Add directories to init.sh
Editscripts/init.sh and add your service’s data directories to the data_dirs array in the create_directories() function:
Set permissions
If your service runs as a specific UID/GID inside the container, add a permissions block in theset_permissions() function:
data/ directory is gitignored. All persistent state (databases, logs, uploads) goes here — never inside apps/<service>/.
Reference: scripts/init.sh — see how IRC, Atheme, XMPP, and The Lounge directories are created.
5. Add a justfile module
Createapps/<service>/justfile with common development commands:
justfile:
just <alias> logs, just <alias> shell, etc.
Reference: apps/bridge/justfile, apps/thelounge/justfile
6. Add documentation
Create a documentation section for your service underapps/docs/content/docs/services/<service>/.
Required pages
At minimum, create an overview page. Add configuration and operations pages if the service has non-trivial setup or operational procedures.services/<service>/index.mdx — overview page:
Navigation metadata
Createservices/<service>/meta.json to control sidebar ordering:
services/meta.json pages array.
Update related pages
- Update the Architecture Overview if your service changes the system topology or adds new inter-service connections
- Update the Ports reference with any new port mappings
- Update the Environment Variables reference with all new variables
7. Add tests
Health check test
Add a health check test totests/unit/ or tests/integration/ that verifies your service starts and responds:
Integration tests
If your service exposes an API or interacts with other services, add integration tests totests/integration/. Use the existing pytest fixtures for Docker Compose orchestration.
Reference: tests/ — see existing test patterns for IRC and bridge services.
Quick reference checklist
Use this as a final check before opening your PR:-
apps/<service>/exists withContainerfileand.dockerignore -
infra/compose/<service>.yamldefines the service with health check - Root
compose.yamlincludes your compose fragment -
docker compose config --quietpasses - New env vars added to
.env.examplewith comments - Config templates added to
scripts/prepare-config.sh(if applicable) -
data/<service>/added toscripts/init.sh -
apps/<service>/justfilecreated and registered in rootjustfile - Documentation pages created under
services/<service>/ -
services/<service>/meta.jsoncreated and parentmeta.jsonupdated - Ports reference updated
- Environment Variables reference updated
- Health check and integration tests added