Infrastructure configuration for the server, managed directly with Docker Compose and Git.
The repository intentionally keeps application-specific configuration out of the root documentation. Each service should document its own setup inside its directory.
.
├── README.md
│
├── caddy/
│ ├── docker-compose.yml
│ ├── Caddyfile
│ └── README.md
│
├── <service>/
│ ├── docker-compose.yml
│ └── README.md
│
└── <service>/
├── docker-compose.yml
└── README.md
The root README documents the infrastructure conventions. Individual service directories document the applications themselves.
The server uses:
- Arch Linux
- Docker Engine
- Docker Compose
- Caddy
- Git
Docker Compose is used independently for each service rather than maintaining one large Compose project.
This keeps services isolated and makes it possible to start, stop, update, or troubleshoot them independently.
A shared external Docker network named proxy is used for services that need to communicate with the reverse proxy.
Create it once:
docker network create proxyCheck that it exists:
docker network lsBecause it is an external network, individual Compose projects must not attempt to create or manage it.
A service that needs to be reachable by the reverse proxy should include:
services:
app:
image: example/image:latest
networks:
- proxy
networks:
proxy:
external: trueThe important parts are:
networks:
- proxyunder the service, and:
networks:
proxy:
external: trueat the Compose level.
The service name becomes the hostname available to other containers on the proxy network.
For example, if the service is named app, the reverse proxy can connect to:
app:<container-port>
Do not use the server's LAN IP for communication between containers when both containers are attached to proxy.
A service does not need to publish its application port to the host when it is only accessed through the reverse proxy.
Prefer:
services:
app:
image: example/image:latest
networks:
- proxyinstead of:
ports:
- "8080:8080"when direct LAN access is unnecessary.
The application port only needs to be exposed inside the container/network.
Publish a host port only when the service genuinely needs direct access from outside Docker.
Caddy is the entry point for HTTP/HTTPS services.
The general flow is:
Client
│
▼
Server :80/:443
│
▼
Caddy
│
▼
proxy network
│
▼
service container
When adding a web service:
- Attach the service to
proxy. - Determine the application's internal listening port.
- Add a route to the Caddy configuration.
- Start the service.
- Reload Caddy.
- Test the route.
Example Caddy configuration:
192.168.0.254 {
handle_path /example/* {
reverse_proxy app:8080
}
}The hostname used by reverse_proxy must match the Compose service name or another resolvable container hostname on the shared network.
Create a directory for the service:
mkdir <service>
cd <service>Create docker-compose.yml.
A basic reverse-proxied service can start with:
services:
app:
image: example/image:latest
restart: unless-stopped
networks:
- proxy
networks:
proxy:
external: trueThen start it:
docker compose up -dCheck it:
docker compose ps
docker compose logs --tail=100Verify that it is connected to the shared network:
docker network inspect proxyAfter configuring Caddy:
cd ~/caddy
docker compose reload caddyUnless a service requires something different, Compose files should generally follow these conventions.
Use:
restart: unless-stoppedfor long-running services.
Use named volumes for persistent application data when appropriate:
services:
app:
image: example/image:latest
volumes:
- app_data:/data
volumes:
app_data:Document important volumes in the service's README.
Prefer an .env file for non-secret configuration when it makes the Compose file easier to maintain:
services:
app:
env_file:
- .envDo not commit passwords, API keys, tokens, or other secrets to Git.
If a service requires an .env file, provide an .env.example containing the required variable names without real credentials.
Each service directory should contain everything necessary to understand and operate that service:
service/
├── docker-compose.yml
├── README.md
├── .env.example
└── other configuration files
Do not place application-specific instructions in the root README.
The service README should explain:
- Purpose
- Image/container information
- Required environment variables
- Ports
- Volumes
- Networks
- Reverse proxy configuration
- Access URL/path
- Backup requirements
- Update procedure
- Troubleshooting
docker compose up -ddocker compose downdocker compose restartdocker compose psdocker compose logs -fdocker compose logs --tail=100docker compose pulldocker compose pull
docker compose up -dFor services that use a local Dockerfile:
docker compose up -d --buildWork from the infrastructure layer upward.
systemctl --failed
df -h
free -hdocker ps
docker info
docker network lsFrom the service directory:
docker compose ps
docker compose logs --tail=100docker network inspect proxyConfirm that the reverse proxy and target service are both attached to the network.
From the reverse proxy directory:
docker compose ps
docker compose logs --tail=100If a proxy route cannot reach a service, first verify:
- The service container is running.
- Both containers are attached to
proxy. - The Compose service name is correct.
- The internal application port is correct.
- The application is actually listening on that port.
Docker Compose files contain infrastructure configuration, but they do not replace application data backups.
Persistent data should be backed up according to the requirements of each service.
Before removing a Docker volume, inspect it carefully:
docker volume ls
docker volume inspect <volume>Never assume that deleting a container also deletes its persistent data, or that deleting a volume is safe.
Service-specific backup instructions belong in that service's README.
The repository is used to version-control infrastructure configuration.
Typical workflow:
git status
git add .
git commit -m "Update infrastructure"
git pushKeep secrets out of Git.
Recommended files to exclude include:
.env
*.secret
Use .env.example files to document required configuration without storing credentials.
This infrastructure intentionally avoids unnecessary management layers.
The preferred model is:
Linux
↓
Docker
↓
Docker Compose
↓
Service
with Caddy providing the shared HTTP/HTTPS entry point when required.
Configuration should remain:
- Explicit
- Version controlled
- Reproducible
- Easy to inspect
- Easy to troubleshoot
- Independent between services
When adding infrastructure, prefer the simplest configuration that satisfies the requirement.