{"id":35153891,"url":"https://github.com/atkaridarshan04/docker-stack","last_synced_at":"2026-04-16T05:04:49.228Z","repository":{"id":325815651,"uuid":"1071098114","full_name":"atkaridarshan04/docker-stack","owner":"atkaridarshan04","description":"A complete containerized DevOps stack with Flask, MySQL, Nginx, and Monitoring Stack","archived":false,"fork":false,"pushed_at":"2026-03-21T06:46:19.000Z","size":5495,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-21T22:42:59.232Z","etag":null,"topics":["alloy","blackbox-exporter","docker","flask","grafana","loki","mysql","nginx","prometheus"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atkaridarshan04.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-06T21:30:58.000Z","updated_at":"2026-03-21T06:46:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/atkaridarshan04/docker-stack","commit_stats":null,"previous_names":["atkaridarshan04/docker-stack"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/atkaridarshan04/docker-stack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atkaridarshan04%2Fdocker-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atkaridarshan04%2Fdocker-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atkaridarshan04%2Fdocker-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atkaridarshan04%2Fdocker-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atkaridarshan04","download_url":"https://codeload.github.com/atkaridarshan04/docker-stack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atkaridarshan04%2Fdocker-stack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31872036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"online","status_checked_at":"2026-04-16T02:00:06.042Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alloy","blackbox-exporter","docker","flask","grafana","loki","mysql","nginx","prometheus"],"created_at":"2025-12-28T16:12:00.928Z","updated_at":"2026-04-16T05:04:49.223Z","avatar_url":"https://github.com/atkaridarshan04.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Docker Stack\n\n\u003cdiv align=\"center\"\u003e\n\n[![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker\u0026logoColor=white)](https://www.docker.com/)\n[![Hadolint](https://img.shields.io/badge/Hadolint-lightblue?logo=docker\u0026logoColor=white)](https://github.com/hadolint/hadolint)\n[![Flask](https://img.shields.io/badge/Flask-000000?logo=flask\u0026logoColor=white)](https://flask.palletsprojects.com/)\n[![MySQL](https://img.shields.io/badge/MySQL-4479A1?logo=mysql\u0026logoColor=white)](https://www.mysql.com/)\n[![NGINX](https://img.shields.io/badge/NGINX-009639?logo=nginx\u0026logoColor=white)](https://nginx.org/)\n[![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?logo=prometheus\u0026logoColor=white)](https://prometheus.io/)\n[![Grafana](https://img.shields.io/badge/Grafana-F46800?logo=grafana\u0026logoColor=white)](https://grafana.com/)\n[![Loki](https://img.shields.io/badge/Loki-F5A623?logo=grafana\u0026logoColor=white)](https://grafana.com/oss/loki/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n*A production-style containerized application stack with full observability*\n\n\u003c/div\u003e\n\n## Overview\n\nThis project is a fully containerized application stack built with production practices in mind. It runs a Flask web application backed by MySQL, served through NGINX as the sole public entry point. On top of that, a complete observability layer is wired in — Prometheus collects metrics from every service, Loki aggregates all container logs, and Grafana provides 7 pre-built dashboards covering the full stack.\n\n## 🏗️ Architecture Overview\n\n![Architecture](assets/architecture/project_arch-light.png)\n\n\n## Dockerfile Linting\n\nHadolint is used to lint `app/Dockerfile` against Dockerfile best-practice rules. Configuration lives in `.hadolint.yaml` at the project root.\n\n### Run hadolint\n\n**Docker (no install needed):**\n```bash\ndocker run --rm -i hadolint/hadolint \u003c app/Dockerfile\n```\n![hadolint output without config](assets/hadolint-no-config.png)\n\n**With the project config applied:**\n```bash\ndocker run --rm -i \\\n  -v \"$(pwd)/.hadolint.yaml:/.config/hadolint.yaml\" \\\n  hadolint/hadolint \u003c app/Dockerfile\n```\n![hadolint output with config](assets/hadolint-with-config.png)\n\n**If hadolint is installed locally:**\n```bash\nhadolint --config .hadolint.yaml app/Dockerfile\n```\n\n### Ignored rule\n\n`DL3008` — *Pin versions in apt-get install* — is intentionally ignored.\n\n---\n\n## Prerequisites\n\n- Docker 20.10+ and Docker Compose v2\n- Secrets files must exist before starting:\n\n```bash\necho -n \"root\"  \u003e secrets/db_root_pw.txt\necho -n \"admin\" \u003e secrets/db_admin_pw.txt\n```\n\n### Start\n\n```bash\n./scripts/start.sh\n```\n\nAll 10 services start in dependency order. MySQL initializes the schema on first boot (`db/init/01_schema.sql`). Flask waits for MySQL to be healthy before starting. NGINX waits for Flask.\n\n### Stop\n\n```bash\n./scripts/stop.sh\n```\n\nContainers are removed, all named volumes (data) are preserved. To also wipe data:\n\n```bash\ndocker compose down -v\n```\n\n### Rebuild the Flask image\n\n```bash\ndocker compose build flask-app\ndocker compose up -d flask-app\n```\n\n### View logs\n\n```bash\ndocker compose logs -f                  # all services\ndocker compose logs -f flask-app mysql  # specific services\n```\n\n### Check service status\n\n```bash\ndocker compose ps\n```\n\n---\n\n## Access\n\n| Interface | URL | Credentials |\n|-----------|-----|-------------|\n| Application | http://localhost | — |\n| Grafana | http://localhost:3000 | `admin` / `admin` |\n| Prometheus | http://localhost:9090 | — |\n| Alloy UI | http://localhost:12345 | — |\n| cAdvisor | http://localhost:8080 | — |\n| mysqld-exporter | http://localhost:9104/metrics | — |\n| Blackbox Exporter | http://localhost:9115 | — |\n\n---\n\n## Observability\n\n### Metrics\n\nPrometheus scrapes 6 targets every 15 seconds with 15-day retention:\n\n| Job | Source | What it measures |\n|-----|--------|-----------------|\n| `flask-app` | Flask `/metrics` | Request rate, latency (p50/p95/p99), error rate per endpoint |\n| `cadvisor` | cAdvisor | Per-container CPU, memory, network, filesystem |\n| `mysqld-exporter` | mysqld-exporter | Query throughput, connections, InnoDB buffer pool, slow queries |\n| `blackbox-http` | Blackbox → NGINX | HTTP uptime, status code, response time phases |\n| `blackbox-exporter` | Blackbox self | Exporter health |\n| `prometheus` | Prometheus self | Scrape health, TSDB stats |\n\nAlloy also collects host-level metrics (CPU, memory, disk, network) via its built-in node exporter and pushes them to Prometheus via remote_write.\n\n### Logs\n\nAlloy tails all container logs via the Docker socket and ships them to Loki with `container` and `service` labels. MySQL slow query log gets special treatment — multiline entries are reassembled and the `query_time` label is extracted for filtering.\n\n### Dashboards\n\n7 pre-provisioned Grafana dashboards, auto-loaded on startup:\n\n| Dashboard | Data Source | What's inside |\n|-----------|-------------|---------------|\n| **Stack Overview** | Prometheus + Loki | Single-pane-of-glass — all service health, links to every other dashboard |\n| **Flask Application** | Prometheus | Request rate, error rate, p50/p95/p99 latency, per-endpoint breakdown |\n| **Docker Containers** | Prometheus | Per-container CPU, memory, network — links to logs |\n| **MySQL** | Prometheus + Loki | Query throughput by type, connections, InnoDB, slow query log panel |\n| **Container Logs** | Loki | Live log viewer, log volume, MySQL slow log, journal errors |\n| **Node Exporter Full** | Prometheus | Full host system metrics |\n| **Prometheus Blackbox Exporter** | Prometheus | HTTP probe status, response time breakdown by phase |\n\nDashboards are cross-linked — container metrics panels link directly to Container Logs filtered to that container.\n\n---\n\n## Secrets\n\nDatabase credentials are managed via Docker Secrets — never plain environment variables.\n\n```\nsecrets/\n├── db_root_pw.txt    # MySQL root password\n└── db_admin_pw.txt   # MySQL app user password (used by Flask + mysqld-exporter)\n```\n\nSee [secrets/README.md](./secrets/README.md).\n\n---\n\n## Project Structure\n\n```\ndocker-stack/\n├── docker-compose.yml\n├── .hadolint.yaml              # Hadolint Dockerfile linting config\n├── app/                        # Flask application\n│   ├── Dockerfile\n│   ├── app.py\n│   ├── requirements.txt\n│   └── templates/index.html\n├── configs/                    # All service configs (mounted read-only)\n│   ├── nginx/default.conf\n│   ├── prometheus/prometheus.yml\n│   ├── loki/loki.yml\n│   ├── alloy/config.alloy\n│   ├── blackbox/blackbox.yml\n│   └── grafana/provisioning/\n│       ├── datasources/        # Prometheus + Loki auto-wired\n│       └── dashboards/json/    # 7 pre-built dashboards\n├── db/init/01_schema.sql       # MySQL schema, auto-run on first start\n├── secrets/                    # Never committed — gitignored\n│   ├── db_root_pw.txt\n│   └── db_admin_pw.txt\n└── scripts/\n    ├── start.sh                # docker compose up -d\n    └── stop.sh                 # docker compose down (volumes preserved)\n```\n\n---\n\n## Screenshots\n\n### Application\n![Flask Application](assets/flask-app.png)\n\n### Prometheus Targets\n![Prometheus Targets](assets/prometheus-targets.png)\n\n### Alloy\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/alloy.png\" alt=\"Alloy Metrics\" width=\"100%\"\u003e\u003c/td\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/alloy-graph.png\" alt=\"Alloy Logs\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Grafana\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/dashboards.png\" alt=\"Dashboards\" width=\"100%\"\u003e\u003c/td\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/datasource.png\" alt=\"Data Sources\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/node-dash.png\" alt=\"Node Exporter\" width=\"100%\"\u003e\u003c/td\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/docker-dash.png\" alt=\"Docker Containers\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/dash-blackbox-exporter.png\" alt=\"Blackbox Exporter\" width=\"100%\"\u003e\u003c/td\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/app-dash.png\" alt=\"App Dashboard\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/drilldown-metrics.png\" alt=\"Metrics Drilldown\" width=\"100%\"\u003e\u003c/td\u003e\n    \u003ctd width=\"50%\"\u003e\u003cimg src=\"assets/grafana/drilldown-logs.png\" alt=\"Log Drilldown\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatkaridarshan04%2Fdocker-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatkaridarshan04%2Fdocker-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatkaridarshan04%2Fdocker-stack/lists"}