2.7 KiB
2.7 KiB
CONTAINER SERVICES (m3-atlas)
Container orchestration with Podman + Traefik reverse proxy
OVERVIEW
11 containerized services on dedicated web network (10.89.0.0/24) with Traefik SSL termination.
STRUCTURE
containers/
├── default.nix # Network setup + service imports
├── baserow.nix # 10.89.0.10 - No-code database
├── ghost.nix # 10.89.0.11 - Blog platform
├── kestra.nix # 10.89.0.12 - Workflow orchestration
├── littlelink.nix # 10.89.0.13 - Link aggregator
├── matomo.nix # 10.89.0.14 - Analytics
├── restreamer.nix # 10.89.0.15 - Video streaming
├── slash.nix # 10.89.0.16 - Link shortener
└── slash-nemoti.nix # 10.89.0.17 - Personal link shortener
WHERE TO LOOK
| Task | Action | Notes |
|---|---|---|
| Add container | Copy existing .nix, increment IP | Must update default.nix imports |
| Fix networking | Check IP conflicts in 10.89.0.0/24 | Gateway always 10.89.0.1 |
| Debug Traefik | Check router rules in service file | Domain must match DNS |
| Access database | Use --add-host=mysql:10.89.0.1 |
Gateway IP for host services |
CONVENTIONS
Container Definition Template
virtualisation.oci-containers.containers.<name> = {
image = "registry/image:tag";
ports = ["127.0.0.1:<external>:<internal>"];
volumes = ["/var/lib/<service>:/data"];
environmentFiles = [config.age.secrets.<name>-env.path];
extraOptions = [
"--network=web"
"--ip=10.89.0.<sequential>"
"--add-host=mysql:10.89.0.1" # If DB needed
];
};
Traefik Integration
services.traefik.dynamicConfigOptions.http = {
services.<name>.loadBalancer.servers = [{
url = "http://127.0.0.1:<port>";
}];
routers.<name> = {
rule = "Host(`<subdomain>.m3ta.dev`)";
service = "<name>";
tls.certResolver = "godaddy";
};
# Legacy redirect (if needed)
routers.<name>-old = {
rule = "Host(`<subdomain>.m3tam3re.com`)";
service = "<name>";
middlewares = ["redirect-m3ta"];
};
};
IP Allocation
- 10.89.0.1: Gateway (host)
- 10.89.0.10-17: Assigned containers
- 10.89.0.18+: Available for new services
ANTI-PATTERNS
- DON'T expose ports publicly - bind to 127.0.0.1 only
- DON'T skip static IP assignment - routing breaks without it
- DON'T hardcode secrets - use age-encrypted env files
- DON'T forget to add imports to default.nix
NOTES
- Network created via activation script in default.nix
- All services behind Traefik - no direct external access
- MySQL/PostgreSQL run on host, accessed via gateway IP
- Secrets pattern:
<service>-env.agewith environment variables