Docker Compose stack for deploying the full NoMercy platform
  • FreeMarker 47.9%
  • Shell 15.5%
  • CSS 14.8%
  • JavaScript 7.1%
  • Java 7%
  • Other 7.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stoney_Eagle 510ba3d378 chore(keycloak): fold the prod theme deploy into deploy-theme.sh --prod
Deploying the theme to prod was three commands typed out each time: pull on the
box, run the script with the prod health URL, check it serves. It drifts, because
the script stamps theme.properties and leaves an unstaged change there, so the
next pull aborts until someone notices and discards it. --prod does the discard,
the pull and the deploy as one step.
2026-08-05 07:08:27 +02:00
autoheal fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
dns feat(dns): cross-connect txt-updater to main stack network for cert pipeline 2026-07-12 19:03:21 +02:00
docker fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
fail2ban fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
keycloak chore(keycloak): fold the prod theme deploy into deploy-theme.sh --prod 2026-08-05 07:08:27 +02:00
logs Add Fail2Ban jail configurations for various services 2025-08-27 18:08:02 +02:00
mysql fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
pgadmin fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
phpmyadmin fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
portainer fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
postgres fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
postgres-kc fix(infra): remove dead cloudflare-token action, cap log size on every service 2026-07-12 18:55:42 +02:00
proxy fix(proxy): point website + dev sites at website_upstream, not bare website 2026-07-14 03:04:17 +02:00
scripts fix(website): seed the idle color's .env from the active checkout 2026-07-14 02:55:49 +02:00
shares feat: Initialize project structure with Docker and environment configuration 2025-05-04 20:48:05 +02:00
website fix(website): seed the notification channels the push validator reads 2026-07-29 03:23:08 +02:00
.env.example fix: infrastructure hardening — memory, healthchecks, security headers, rate limiting 2026-04-02 20:35:27 +02:00
.gitattributes chore: add .gitattributes for consistent line endings 2026-03-16 05:51:38 +01:00
.gitignore feat(keycloak): one combined login screen and a real consent screen 2026-08-02 06:52:49 +02:00
.gitmodules feat: Add submodule for website data repository 2025-05-07 07:17:38 +02:00
docker-compose.yml fix(website): prevent cpu-starvation outages on the homepage 2026-06-04 21:57:18 +02:00
README.md feat(website): blue-green zero-downtime deploy behind nginx 2026-07-12 21:45:23 +02:00
yarn.lock fix: update repo references to new kebab-case names and infra tweaks 2026-03-10 16:05:00 +01:00

nomercy-stack

A multi-service development stack using Docker Compose, including MySQL, PostgreSQL, Keycloak, phpMyAdmin, pgAdmin, Portainer, Nginx proxy, and a website container. This stack is designed for local development and testing.

Prerequisites

Setup Instructions

  1. Clone the repository

    git https://github.com/NoMercy-Entertainment/nomercy-stack.git
    cd nomercy-stack
    
  2. Provide SSL Certificates

    • Place your SSL certificate files (cert.pem and key.pem) in the proxy/certificates/ directory.
    • These files are required for the Nginx proxy to serve HTTPS traffic.
  3. Configure Domain and Environment Variables

    • Copy .env.example to .env if provided, and adjust values as needed.
    • Some services may require additional configuration in their respective folders.
  4. Access Services

    Replace example.com with your actual domain as configured in the proxy/sites/ conf files.

  5. Start the Stack

    docker compose up -d
    

    This will start all services defined in docker-compose.yml and referenced compose files.

Enabling Keycloak Login for Portainer

To enable Keycloak authentication in Portainer:

  1. Log in to Portainer at https://portainer.example.com as an admin.

  2. Go to Settings > Authentication.

  3. Select OAuth as the authentication method.

  4. Enter the following values (replace example.com with your actual domain):

    • Client ID: master
    • Client Secret: ******* (your Keycloak client secret)
    • Authorization URL: https://auth.example.com/realms/master/protocol/openid-connect/auth
    • Access Token URL: https://auth.example.com/realms/master/protocol/openid-connect/token
    • Resource URL: https://auth.example.com/realms/master/protocol/openid-connect/userinfo
    • Redirect URL: https://portainer.example.com
    • Logout URL: https://auth.example.com/realms/master/protocol/openid-connect/logout?redirect_uri=https://portainer.example.com/#!/auth
    • User Identifier: email
    • Scopes: openid profile email
    • Auth Style: (leave as default or as required by your setup)
  5. Save the settings. You should now be able to log in to Portainer using Keycloak.

Folder Structure

  • docker/ - Additional compose files for Cloudflared tunnel and GitHub runner.
  • keycloak/ - Keycloak service and custom themes
  • mysql/ - MySQL service and data
  • pgadmin/ - pgAdmin service
  • phpmyadmin/ - phpMyAdmin service
  • portainer/ - Portainer service and data
  • postgres/ - PostgreSQL service and data
  • postgres-kc/ - Separate PostgreSQL for Keycloak
  • proxy/ - Nginx reverse proxy, configs, certificates, and site definitions
  • website/ - Website container (PHP, Nginx, etc.)
  • shares/ - Shared files (if any)
  • scripts/ - Host provisioning + maintenance scripts (see below)

Host provisioning scripts (production droplet)

These install host-level config that lives outside any container (docker daemon, system logrotate) so it survives a fresh droplet provision. Run as root from the stack root on the target host, and re-run any time after pulling changes to docker/daemon.json or scripts/logrotate/:

./scripts/provision-docker-daemon-logging.sh   # caps every container's json-file log (max-size 20m x max-file 5)
./scripts/provision-nginx-logrotate.sh         # rotates the bind-mounted logs/nginx/*.log files

scripts/disk-cleanup.sh runs daily via cron (/etc/cron.d or crontab -l on the host) to prune Docker build cache/images and vacuum the journal.

Zero-downtime website deploys (blue-green)

Production deploys of the website no longer stop-and-recreate a single container. scripts/deploy-website.sh (invoked by nomercy-tv's ci.yml deploy job) builds and boots the idle color (website-blue / website-green, see website/website-compose.yml), health-gates it (docker healthcheck + a real curl /up from inside the container), and only then flips the nginx upstream to it with nginx -s reload. A graceful reload finishes in-flight requests on the old upstream and sends new connections to the new one — the :80/:443 listener socket never closes, so there is no refused-connection window. The previous color drains for 15s then gets stopped (not removed), ready to be next deploy's idle target.

State lives in two gitignored, server-local files:

  • website/.active-color — which color is currently live.
  • proxy/sites/upstream-website.conf — what nginx currently points at (generated from proxy/sites/upstream-website.conf.default, kept in sync with the marker above by the deploy script). Never hand-edit or commit this file; it's overwritten on every deploy.

Expand/contract migrations are required. Blue and green share one database, and both are live against it during the health-gate + drain window — the outgoing color keeps serving real traffic against a possibly-already-migrated schema until the switch and drain complete. Any migration shipped through this pipeline must be backward compatible with the code still running in the outgoing color: additive columns/tables/indexes only. A breaking change (rename, drop, type change, new NOT NULL on an existing column) has to be split into an expand deploy (add the new shape, dual read/write) that soaks first, followed by a later contract deploy (remove the old shape) once you've confirmed no color still needs it. The deploy script does not detect or block breaking migrations — that's a migration-review call, not an infra one.

Rollback: if the idle color never reports docker-healthy or /up never returns 200, the script exits non-zero, nginx is never touched, and the previously-active color keeps serving. The unhealthy idle container is left running (not removed) for docker logs/docker inspect triage; stop it once diagnosed and re-run the script to retry. If a regression is found after a successful automated switch (health check passed but the release has a business-logic bug), roll back manually: edit proxy/sites/upstream-website.conf back to the previous color, then docker exec nomercy.tv-proxy nginx -t && docker exec nomercy.tv-proxy nginx -s reload, and flip website/.active-color back to match — the previous color's container is still there (stopped) if it needs a docker compose start.

One-time cutover: the very first run of this script on a server still running the old single website container detects it, stops it, and starts website-blue in its place, reusing the same ./data bind mount and vendor/node_modules volumes. That one recreate is a normal few-second blip (same class as the old deploy), not a zero-downtime switch — plan it for a low-traffic window. Every deploy after that is a real blue-green switch.

Shares

Every folder placed in the shares/ directory will automatically become available as a subdomain. For example, if you add a folder named docs inside shares/, it will be accessible at https://docs.example.com (replace example.com with your actual domain).

This allows you to easily expose additional static sites or resources as separate subdomains by simply adding folders to the shares/ directory.

Notes

  • Data folders (e.g., mysql/data/, postgres/data/) are git-ignored for safety and performance.
  • Custom Keycloak themes can be placed in keycloak/themes/<your theme name>/.
  • Configuration files for each service are in their respective folders.
  • PHPMyAdmin has a keycloak login flow

Troubleshooting

  • Ensure no other services are running on the same ports.
  • Check logs with docker compose logs <service> for debugging.
  • For permission issues on data folders, ensure Docker has access to your drive.

Contact

For further information or support, visit NoMercy.tv or contact our support team.

Made with ❤️ by NoMercy Entertainment


Feel free to customize this stack for your development needs!