{"id":29178346,"url":"https://github.com/aleixnguyen-vn/docker-wordpress-performance","last_synced_at":"2026-04-12T18:02:53.137Z","repository":{"id":308644265,"uuid":"1010431450","full_name":"aleixnguyen-vn/docker-wordpress-performance","owner":"aleixnguyen-vn","description":"WordPress Docker stack serving 5,000 users at 187ms on a 1GB VPS with Redis \u0026 Caddy.","archived":false,"fork":false,"pushed_at":"2025-08-07T02:36:05.000Z","size":1353,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-07T04:22:57.400Z","etag":null,"topics":["caddy","case-study","docker","docker-compose","infrastructure","nginx","performance-optimization","php-fpm","redis","vps","vultr","wordpress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aleixnguyen-vn.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}},"created_at":"2025-06-29T03:57:19.000Z","updated_at":"2025-08-07T02:36:08.000Z","dependencies_parsed_at":"2025-08-07T04:23:00.999Z","dependency_job_id":"0d3eaa60-b2ee-4692-a7dd-8fe29b78b3db","html_url":"https://github.com/aleixnguyen-vn/docker-wordpress-performance","commit_stats":null,"previous_names":["aleixnguyen-vn/docker-wordpress-performance"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/aleixnguyen-vn/docker-wordpress-performance","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleixnguyen-vn%2Fdocker-wordpress-performance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleixnguyen-vn%2Fdocker-wordpress-performance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleixnguyen-vn%2Fdocker-wordpress-performance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleixnguyen-vn%2Fdocker-wordpress-performance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aleixnguyen-vn","download_url":"https://codeload.github.com/aleixnguyen-vn/docker-wordpress-performance/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleixnguyen-vn%2Fdocker-wordpress-performance/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273879446,"owners_count":25184427,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"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":["caddy","case-study","docker","docker-compose","infrastructure","nginx","performance-optimization","php-fpm","redis","vps","vultr","wordpress"],"created_at":"2025-07-01T18:46:07.201Z","updated_at":"2025-10-25T01:09:54.196Z","avatar_url":"https://github.com/aleixnguyen-vn.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Benchmark](https://img.shields.io/badge/Benchmark-5000_users,_189ms_avg-blue?style=for-the-badge\u0026logo=loader.io)\n![Redis Hit](https://img.shields.io/badge/Redis_Hit_Rate-99.93%25-brightgreen?style=for-the-badge\u0026logo=redis\u0026logoColor=white)\n![Lighthouse](https://img.shields.io/badge/Lighthouse_Score-93/100-yellow?style=for-the-badge\u0026logo=googlechrome)\n\n![Built With Docker](https://img.shields.io/badge/Built_with-Docker-blue?style=for-the-badge\u0026logo=docker)\n![NGINX + PHP-FPM](https://img.shields.io/badge/Stack-NGINX_+_PHP--FPM-informational?style=for-the-badge\u0026logo=nginx)\n![Redis Cache](https://img.shields.io/badge/Cache-Redis-red?style=for-the-badge\u0026logo=redis)\n![Caddy Server](https://img.shields.io/badge/SSL-Caddy-green?style=for-the-badge\u0026logo=letsencrypt)\n![VPS](https://img.shields.io/badge/Deployed_on-Vultr-blue?style=for-the-badge\u0026logo=vultr)\n![Ubuntu](https://img.shields.io/badge/Ubuntu-22.04_LTS-E95420?style=for-the-badge\u0026logo=ubuntu\u0026logoColor=white)\n\n\n# 🎈 [CASE STUDY] WordPress on Docker: 5000 Client Benchmark on 1GB RAM VPS\n\n\u003e **\"Optimizing WordPress at this level isn’t about plugins — it’s about removing bottlenecks one by one.\"**\n\n\u003e ⚠️ Disclaimer: This repo focuses solely on the technical side – site architecture, deployment, and performance tuning skills.\n\u0026#x20;\u0026#x20;\n\n---\n\n## 1. 📌 Objectives\n\n- Run WordPress with Docker on a basic VPS (1vCPU, 1GB RAM)\n- Serve 5000 concurrent clients/minute\n- Achieve 189ms average response time\n- Use only free or open-source stack (Docker, Caddy, Redis, MariaDB)\n\n---\n\n## 2. ⚙️ Stack Overview\n\n- **VPS**: Vultr 6$ VPS (1vCPU, 1GB RAM, 25GB SSD NVMe)\n- **OS:** Ubuntu 22.04 LTS\n- **Web Server:** NGINX (behind Caddy for HTTPS)\n- **CMS:** WordPress (php8.2-fpm)\n- **DB:** MariaDB 10.5\n- **Cache:** Redis (Object Cache)\n- **SSL Proxy:** Caddy (reverse proxy + HTTP/3)\n- **CDN:** Cloudflare (free plan)\n\n---\n\n## 3. 💪 Key Optimizations\n\n### 3.1 PHP-FPM Pool (wp-app)\n\n```ini\npm = dynamic\npm.max_children = 4\npm.start_servers = 2\npm.min_spare_servers = 1\npm.max_spare_servers = 3\npm.max_requests = 500       ; auto recycle to avoid memory leak\n\n\n; increased php execution timeout \nrequest_terminate_timeout = 30s\n```\n\n### 3.2 OPCache\n\n```ini\nopcache.enable=1\nopcache.enable_cli=1\nopcache.memory_consumption=128\nopcache.interned_strings_buffer=16\nopcache.max_accelerated_files=10000\nopcache.validate_timestamps=0  ; if dont require hot reload file\nopcache.revalidate_freq=60\n```\n\n### 3.3 Redis Configuration\n\n```ini\nmaxmemory 256mb\nmaxmemory-policy allkeys-lru\n```\n\n- Redis as object cache for WordPress\n- Hit rate: **99.93%**\n- **No manual preload yet**, cache populated purely through real traffic\n\n### 3.4 NGINX Performance\n\n```nginx\ngzip on;\ngzip_disable \"msie6\";\n\ngzip_vary on;\ngzip_proxied any;\ngzip_comp_level 5;               # 1 - 9(5 for best performance)\ngzip_buffers 16 8k;\ngzip_http_version 1.1;\ngzip_min_length 256;\n\ngzip_types\n    text/plain\n    text/css\n    application/json\n    application/javascript\n    application/x-javascript\n    text/xml\n    application/xml\n    application/xml+rss\n    image/svg+xml;\n```\n\n### 3.5 Caddy for SSL\n\n```text\nexample.com {\n    reverse_proxy nginx:80 {\n        header_up X-Forwarded-Proto https\n    }\n\n    encode gzip\n\n    @static {\n        path_regexp \\.(jpg|jpeg|png|gif|ico|css|js|woff2?|ttf|svg)$\n    }\n\n    header @static {\n        Cache-Control \"public, max-age=31536000\"\n        Expires \"Sun, 31 Dec 2037 23:55:55 GMT\"\n    }\n}\n```\n\n---\n\n## 4. 📊 Benchmark Results (Loader.io)\n\n### Scenario:\n\n- 5000 clients over 1 minute\n- All requests to homepage (cached via Redis)\n\n| Metric            | Value                     |\n| ----------------- | ------------------------- |\n| Avg Response Time | **189ms**                 |\n| Redis Hit Rate    | **99.93%**                |\n| Success Rate      | **100%**                  |\n| Max Latency       | 486ms                       |\n| RAM Usage (peak)  | **\\~451MB**               |\n| CPU               | 2-6% |\n| Errors / Timeouts | **0**                     |\n\n\u003e 🚀 Even at 5000 users/minute, no Redis or MySQL bottlenecks were observed. Cache was warmed purely by live traffic.\n\n---\n\n## 5. 🏆 Summary\n\n- Dockerized WordPress stack on 1vCPU/1GB RAM VPS\n- No paid services: all open-source or free-tier\n- Redis cache hit 241,595 / 179 → **99.93%** hit rate\n- Caddy offloaded SSL + HTTP/3\n- Cloudflare added as CDN layer (compression + edge cache)\n\n### Result:\n\n\u003e 🌟 **5000 concurrent users** served in 1 minute at **187ms average**, no crash, no errors. RAM used: **\\~431MB**.\n\n---\n\n## 6. 🖼 Some screenshots\n\nBelow are key screenshots capturing performance results and system metrics during the tests.\n\n### 🔹 1. Loader.io Benchmark – 5000 Users,\n\n#### 🧪 Scene 1 – Warm cache, optimal performance\n![Loader.io benchmark 5000 users 187ms](screenshots/loaderio-5000users-valid-187ms.png)\n\u003e**Final round**:\u003cbr\u003e\n5,000 concurrent users · 187ms avg · 0% errors · 100% valid redirects\u003cbr\u003e\nMax latency only 391ms — demonstrates steady-state performance under full load.\n\n#### 🧊 Scene 2 – Cold start (post page)\n\n![Loader.io benchmark 5000 cold start 200ms](screenshots/loader_cold_start_5000.png)\n\u003eReal-world uncached access: 198ms avg, 0% errors\nShows how the stack handles cache-warming and first-hit scenarios with zero degradation.\n\n---\n\n### 🔹 2. 93 Google Lighthouse Score\n\n![Google Lighthouse](screenshots/google_lighthouse.png)\n\u003eScore: 93/100 (Desktop)\nMinimal layout shift, optimized loading.\ncf-cache-status: HIT, HTML gzip, no render-blocking JS.\n\n\u003e ⚠️ Note: idk why Pingdom score stuck at **87/100** despite full gzip and cache header tuning — likely due to CDN location and test heuristics.\n\n---\n\n### 🔹 3. Redis Hit Rate\n\n![Redis CLI hit rate](screenshots/redis_cli_info.png)\n*241,595 cache hits vs. 179 misses — 99.93% hit rate without preload.*\n\n---\n\n### 🔹 4. Docker Stats + `htop` During Load Test\n\n![Docker Stats](screenshots/docker_stats_5000.png)\n![htop](screenshots/htop_during_bench.png)\n\n\u003eRAM usage: ~431MB peak\u003cbr\u003e\nAll services stayed under control, including PHP-FPM, Redis, MariaDB\u003cbr\u003e\nSwap barely touched, CPU stable at 2–6%\n\n---\n\n### 🔹 5. Chrome DevTools: Network Tab\n\n![TTFB + headers](screenshots/chrome_network_tab.png)\n\u003e✅ cf-cache-status: HIT\u003cbr\u003e\n✅ gzip enabled\u003cbr\u003e\n✅ HTTP/3 from Caddy\u003cbr\u003e\nTTFB under 200ms, total load \u003c2s across 67 assets.\n\n---\n\n### 🔹 6. Folder Structure / Project Layout\n\n![Repo layout](screenshots/folder_stucture.png)\n*Modular Docker-based layout with isolated services and shared volumes.*\n\n### 🖥 7. Server Specs – Vultr 1GB VPS\n\n![Vultr server overview](screenshots/vultr_1gb_ram_frankfurt.png)  \n\u003e Benchmark environment: Vultr 1vCPU / 1GB RAM VPS (Frankfurt). Stack deployed on Ubuntu 22.04 with Docker. Current cost: $0.07.*\n\n---\n\n_For more screenshots of alternative configs and test iterations, browse the full `/screenshots` folder in the repository._\n\n## 7. 📆 Future Improvements\n\n- Add FastCGI Cache (NGINX-level full page caching)\n- Integrate GitHub Actions to trigger Redis preload after deploy\n- Explore ESI + Edge Caching (via QUIC.cloud or Cloudflare Workers)\n- Auto-monitor Redis hit rate and auto-flush if fallback detected\n\n---\n\n## 8. 💼 Repository \u0026 Source Code\n\n\u003e [https://github.com/aleixnguyen-vn/docker-wordpress-performance](https://github.com/aleixnguyen-vn/docker-wordpress-performance)\n\n---\n\n\u003e ⏱️ I picked up Docker at 2PM. This stack was live — and benchmarking — before midnight.\n\u003e 🔊 \"You don't need a bigger server. You need better config.\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleixnguyen-vn%2Fdocker-wordpress-performance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faleixnguyen-vn%2Fdocker-wordpress-performance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleixnguyen-vn%2Fdocker-wordpress-performance/lists"}