Private LAN platform for one Debian/Ubuntu box: HTTPS portal, message queue, cache, databases, Docker registry, CI runner, Maven and Debian repos. Ansible + Docker Compose, no cloud.
Host: devops.local. Portal: https://devops.local/ (English by default, EN/RU toggle). Trust the CA once: http://devops.local/ca.crt.
| RabbitMQ | PostgreSQL · Adminer |
|---|---|
![]() |
![]() |
| MongoDB · express | Maven · Reposilite |
|---|---|
![]() |
![]() |
Web UIs sit behind nginx HTTPS. AMQP / MQTT / Redis / Postgres / Mongo / Elasticsearch stay on native ports.
| URL | |
|---|---|
| Portal | https://devops.local/ |
| CA | http://devops.local/ca.crt |
| Portainer | https://devops.local/portainer/ (admin) |
| Registry UI | https://devops.local/registry/ |
| Registry API | https://devops.local/v2/ · docker login devops.local |
| RabbitMQ | https://devops.local/rabbitmq/ · AMQP :5672 · MQTT :1883 · WS :15675 |
| Redis Commander | https://devops.local/redis/ · Redis :6379 |
| PostgreSQL | https://devops.local/postgres/ · :5432 |
| MongoDB | https://devops.local/mongo/ · :27017 |
| Elasticsearch | https://devops.local/elastic/ · :9200 |
| Maven | https://devops.local/maven/ |
| Debian APT | https://devops.local/debian/ |
| Gitea MCP | http://devops.local:8080/mcp |
| Local Gitea | http://devops.local:3000/ (optional, GITEA_ENABLED=true) |
Docs: Maven / APT · databases · TLS / docker login · Gitea.
docker login devops.local
docker tag myimg devops.local/myimg
docker push devops.local/myimg| Stack | On the host | What it runs |
|---|---|---|
devops |
/opt/compose/devops |
runner, MCP, Maven, Debian, registry, Portainer, TLS/CA; local Gitea if GITEA_ENABLED=true |
middleware |
/opt/compose/middleware |
nginx portal :80→:443, RabbitMQ (+MQTT), Redis, Postgres, Mongo, ES |
Each service is optional (make setup toggles / .env *_ENABLED). Compose profiles follow those flags.
Runner / MCP always use gitea_instance_url (existing Synology Gitea) — they are not rewritten to :3000. See docs/gitea.md.
Data lives on the host (bind mounts, not named volumes):
| Path | Contents |
|---|---|
/opt/compose/tls |
Private CA + nginx server cert (owned by devops) |
/opt/compose/middleware/data/rabbitmq |
RabbitMQ |
/opt/compose/middleware/data/redis |
Redis AOF/RDB |
/opt/compose/middleware/data/postgres |
PostgreSQL |
/opt/compose/middleware/data/mongo |
MongoDB |
/opt/compose/middleware/data/elasticsearch |
Elasticsearch |
/opt/compose/devops/data/runner |
act_runner state |
/opt/compose/devops/data/maven |
Reposilite |
/opt/compose/devops/data/debian |
reprepro pool / dists / incoming |
/opt/compose/devops/data/gitea |
Local Gitea (SQLite + repos) |
/opt/compose/devops/data/debian-gpg |
APT signing key |
/opt/compose/devops/data/registry |
Registry blobs |
/opt/compose/devops/data/portainer |
Portainer |
First deploy: Ansible creates host bind-mount dirs under /opt/compose/{devops,middleware}/data.
Ansible runs in WSL. On Windows, ansible.ps1 wraps wsl make. Native Windows is not a supported control node.
Prerequisites: WSL with make + python3-venv, SSH key to darvik@devops.local, sudo on the target.
# WSL
sudo apt update && sudo apt install -y make python3-venv python3-pip
cd /mnt/d/Projects/devops/dockerDo not edit tracked inventory for secrets. make setup writes a gitignored .env (component toggles, credentials, optional CA paths); deploy targets read it via .env.yml + inventory/local.ini.
make setup # components + secrets + optional bring-your-own CA
make ping
make docker # Docker CE + Compose plugin
make devops # TLS + registry/portainer/runner/… (before middleware)
make middleware # portal + data services
make deploy # all of the above (site.yml)Windows:
.\ansible.ps1 setup
.\ansible.ps1 ping
.\ansible.ps1 docker
.\ansible.ps1 devops
.\ansible.ps1 middleware
.\ansible.ps1 deployOther targets: make install, make check, make version, make clean, make help.
The Makefile uses project .venv and sets ANSIBLE_CONFIG / ANSIBLE_ROLES_PATH / ANSIBLE_INVENTORY (needed on world-writable /mnt/d/...).
Playbook issues a private CA + server cert, installs the CA into OS trust and /etc/docker/certs.d/ on devops.local, and does not use insecure-registries. LAN clients need the same CA (docs/registry-tls.md).
Portainer: user admin, password from .env (PORTAINER_PASSWORD, min 12 chars). --admin-password-file only seeds the first init. If login fails after a previous deploy, set PORTAINER_FORCE_REINIT=true once, make devops, then set it back to false (wipes Portainer data).
make devops issues TLS under /opt/compose/tls and brings up registry/portainer/runner/….
New portal card: add portal_services in inventory/group_vars/middleware.yml and, if needed, location = /status/... in stacks/middleware/nginx/default.conf.j2. Bilingual copy: portal_i18n + title / description { en, ru }.
Refresh screenshots (needs .env and a reachable portal; portal is forced to English):
npm install
npx playwright install chromium
PORTAL_URL=https://devops.local npm run screenshotsCursor MCP:
{
"mcpServers": {
"gitea": {
"url": "http://devops.local:8080/mcp",
"headers": {
"Authorization": "Bearer <gitea_mcp_access_token>"
}
}
}
}Credentials and host/URL overrides: .env (make setup). Defaults live in inventory/group_vars/ — do not put secrets there.
| Variable | Default | Description |
|---|---|---|
gitea_enabled |
false |
Local Gitea. .env: GITEA_ENABLED |
runner_enabled / mcp_enabled / maven_enabled / debian_enabled |
true |
DevOps toggles |
registry_enabled / portainer_enabled |
true |
Registry + Portainer (devops stack) |
rabbitmq_enabled / redis_enabled / postgres_enabled / mongo_enabled / elastic_enabled |
true |
Middleware toggles |
portal_lang |
en |
Portal default language (en / ru); UI toggle still works |
tls_ca_source_crt / tls_ca_source_key |
`` | Optional local CA paths (from make setup) |
gitea_instance_url |
Synology URL | Runner/MCP target. Not rewritten to local :3000 |
gitea_http_port / gitea_ssh_port |
3000 / 2222 |
Local Gitea HTTP / SSH |
gitea_root_url |
http://devops.local:3000/ |
Local server ROOT_URL only |
gitea_runner_name |
devops.local |
Runner name in Gitea |
gitea_runner_labels |
devops.local:docker://catthehacker/ubuntu:act-latest |
Runner labels |
gitea_mcp_port |
8080 |
Host port for gitea-mcp HTTP |
maven_http_port |
8083 |
Reposilite on localhost (nginx /maven/) |
debian_http_port |
8084 |
Debian www on localhost (nginx /debian/) |
gitea_runner_force_reregister |
false |
Delete .runner and register again |
middleware_http_port |
80 |
Nginx HTTP → HTTPS redirect + /ca.crt |
middleware_https_port |
443 |
Nginx portal, UIs, registry /v2/ |
tls_enabled |
true |
Private CA + server cert for nginx |
tls_extra_dns / tls_extra_ips |
[] |
Extra SANs on the server cert |
rabbitmq_amqp_port |
5672 |
RabbitMQ AMQP |
rabbitmq_mqtt_port |
1883 |
RabbitMQ MQTT (same plugins as iot-platform-go) |
rabbitmq_mqtt_ws_port |
15675 |
RabbitMQ MQTT over WebSocket |
redis_port |
6379 |
Redis |
postgres_port |
5432 |
PostgreSQL |
mongo_port |
27017 |
MongoDB |
elastic_http_port |
9200 |
Elasticsearch HTTP |
elastic_java_opts |
-Xms512m -Xmx512m |
ES heap |
compose_root |
/opt/compose |
Remote directory for stacks |
Do not commit .env, .env.yml, or inventory/local.ini.
- Add
stacks/<name>/docker-compose.ymland.env.j2 - Add an inventory group and playbook that applies
compose_stackwithcompose_stack_name: <name>
Old passlib + bcrypt 5.x is incompatible. This repo hashes via scripts/bcrypt_hash.py. Reinstall venv deps:
make install
make middlewareBroken system Ansible. Use the Makefile venv only:
make clean
make install
make deployThe runner keeps a hidden /opt/compose/devops/data/runner/.runner after first register. If the token was reset or the runner was removed in Gitea UI, Declare returns 500. Registration env vars are ignored while .runner exists.
ssh darvik@devops.local
sudo docker compose -f /opt/compose/devops/docker-compose.yml stop devops-runner
sudo rm -f /opt/compose/devops/data/runner/.runner
sudo docker compose -f /opt/compose/devops/docker-compose.yml up -d devops-runnerOr set GITEA_RUNNER_FORCE_REREGISTER=true once and make devops, then set it back to false. Use a fresh token from Gitea → Site Administration → Actions → Runners.
ssh darvik@devops.local
sudo docker compose -f /opt/compose/devops/docker-compose.yml ps
sudo docker compose -f /opt/compose/devops/docker-compose.yml logs
sudo docker compose -f /opt/compose/middleware/docker-compose.yml ps
sudo docker compose -f /opt/compose/middleware/docker-compose.yml logs




