{"id":50828862,"url":"https://github.com/kpirnie/infra-build","last_synced_at":"2026-06-13T21:02:30.960Z","repository":{"id":360255426,"uuid":"1248246660","full_name":"kpirnie/infra-build","owner":"kpirnie","description":"Production-grade, security-hardened Docker/Podman images built from source on Alpine Linux. Rebuilt nightly via GitHub Actions and published to GHCR.","archived":false,"fork":false,"pushed_at":"2026-05-25T18:12:23.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T18:12:48.810Z","etag":null,"topics":["docker","nginx","php","podman"],"latest_commit_sha":null,"homepage":"https://kevinpirnie.com/","language":"Dockerfile","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/kpirnie.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":"2026-05-24T11:42:43.000Z","updated_at":"2026-05-25T18:12:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kpirnie/infra-build","commit_stats":null,"previous_names":["kpirnie/infra-build"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kpirnie/infra-build","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpirnie%2Finfra-build","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpirnie%2Finfra-build/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpirnie%2Finfra-build/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpirnie%2Finfra-build/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kpirnie","download_url":"https://codeload.github.com/kpirnie/infra-build/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpirnie%2Finfra-build/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34300116,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"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":["docker","nginx","php","podman"],"created_at":"2026-06-13T21:02:30.243Z","updated_at":"2026-06-13T21:02:30.952Z","avatar_url":"https://github.com/kpirnie.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Infra-Build\r\n\r\n[![Build](https://img.shields.io/github/actions/workflow/status/kpirnie/infra-build/nginx.yml?branch=main\u0026label=Build\u0026logoColor=white\u0026logo=github\u0026labelColor=000\u0026style=for-the-badge)](https://github.com/kpirnie/infra-build/actions/workflows/nginx.yml)\r\n[![Issues](https://img.shields.io/github/issues/kpirnie/infra-build?style=for-the-badge\u0026logo=github\u0026color=006400\u0026logoColor=white\u0026labelColor=000)](https://github.com/kpirnie/infra-build/issues)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-orange.svg?style=for-the-badge\u0026logo=opensourceinitiative\u0026logoColor=white\u0026labelColor=000)](LICENSE)\r\n[![Kevin Pirnie](https://img.shields.io/badge/-KevinPirnie.com-000d2d?style=for-the-badge\u0026labelColor=000\u0026logoColor=white\u0026logo=data:image/svg%2Bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+CiAgPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiLz4KICA8ZWxsaXBzZSBjeD0iMTIiIGN5PSIxMiIgcng9IjQuNSIgcnk9IjEwIi8+CiAgPGxpbmUgeDE9IjIiIHkxPSIxMiIgeDI9IjIyIiB5Mj0iMTIiLz4KICA8bGluZSB4MT0iNC41IiB5MT0iNi41IiB4Mj0iMTkuNSIgeTI9IjYuNSIvPgogIDxsaW5lIHgxPSI0LjUiIHkxPSIxNy41IiB4Mj0iMTkuNSIgeTI9IjE3LjUiLz4KPC9zdmc+Cg==)](https://kevinpirnie.com/)\r\n\r\nProduction-grade, security-hardened Docker/Podman images built from source on Alpine Linux. Rebuilt nightly via GitHub Actions and published to GHCR.\r\n\r\n---\r\n\r\n## Images\r\n\r\n### nginx\r\n\r\n```\r\nghcr.io/kpirnie/nginx:latest\r\nghcr.io/kpirnie/nginx:latest-YYYY-MM-DD\r\n```\r\n\r\n**Built with:**\r\n\r\n| Feature | Detail |\r\n|---|---|\r\n| Base | `alpine:latest` |\r\n| nginx | Mainline, compiled from source |\r\n| TLS | [OpenSSL 4.x](https://github.com/openssl/openssl) — native QUIC support, statically linked |\r\n| HTTP/3 | QUIC via `--with-http_v3_module` |\r\n| HTTP/2 | `--with-http_v2_module` |\r\n| Compression | Brotli (`ngx_brotli`), Zstd (`zstd-nginx-module`), gzip (built-in) |\r\n| GeoIP | GeoIP2 (`ngx_http_geoip2_module`) — MaxMind databases must be volume-mounted |\r\n| Headers | `headers-more-nginx-module` |\r\n| Scripting | NJS (`njs`) |\r\n| Image processing | `--with-http_image_filter_module` |\r\n| Stream proxy | `--with-stream` + SSL + realip + ssl_preread |\r\n| Arch | `linux/amd64`, `linux/arm64` |\r\n| Runs as | root (master) → `nginx` UID 101 (workers) |\r\n\r\n---\r\n\r\n### PHP-FPM\r\n\r\n```\r\nghcr.io/kpirnie/php:8.2-latest\r\nghcr.io/kpirnie/php:8.2-latest-YYYY-MM-DD\r\n\r\nghcr.io/kpirnie/php:8.3-latest\r\nghcr.io/kpirnie/php:8.4-latest\r\nghcr.io/kpirnie/php:8.5-latest\r\n```\r\n\r\n**Built with:**\r\n\r\n| Feature | Detail |\r\n|---|---|\r\n| Base | `php:8.x-fpm-alpine` |\r\n| Versions | 8.2 · 8.3 · 8.4 · 8.5 |\r\n| Arch | `linux/amd64`, `linux/arm64` |\r\n| Runs as | root (master) → `www-data` UID 82 (workers) |\r\n\r\n**Extensions:**\r\n\r\n`apcu` `bcmath` `calendar` `exif` `gd` `gettext` `igbinary` `imagick` `intl` `msgpack` `mysqli` `opcache` `pcntl` `pdo_mysql` `pdo_pgsql` `pgsql` `redis` `sodium` `sockets` `tidy` `uuid` `xsl` `yaml` `zip`\r\n\r\nPlus all default extensions bundled in the official PHP Alpine image: `curl` `dom` `fileinfo` `iconv` `mbstring` `openssl` `pdo` `phar` `simplexml` `tokenizer` `xml` `xmlreader` `xmlwriter` `zlib` and others.\r\n\r\n`redis` is compiled with `igbinary` and `msgpack` serializer support.\r\n\r\n**Tools:** WP-CLI · Composer\r\n\r\n---\r\n\r\n## Configuration\r\n\r\nBoth images ship a minimal base config. Everything else is expected to be volume-mounted.\r\n\r\n### nginx\r\n\r\n| Mount | Purpose |\r\n|---|---|\r\n| `/etc/nginx/conf.d/` | HTTP site configs (`*.conf`) |\r\n| `/etc/nginx/stream.d/` | Stream proxy configs (`*.conf`) |\r\n| `/etc/nginx/sites-enabled/` | Alternative site config location |\r\n| `/var/log/nginx/` | Access and error logs |\r\n| `/path/to/GeoIP2/` | MaxMind `.mmdb` database files — point `geoip2` directives at your mount path |\r\n\r\n### PHP-FPM\r\n\r\n| Mount | Purpose |\r\n|---|---|\r\n| `/usr/local/etc/php/conf.d/` | `php.ini` override snippets (`*.ini`) |\r\n| `/usr/local/etc/php-fpm.d/` | FPM pool override configs |\r\n| `/var/log/php-fpm/` | FPM logs |\r\n\r\n\u003e **`disable_functions`** — empty by default. Add your own restrictions via a volume-mounted snippet in `/usr/local/etc/php/conf.d/`. Note that entries can only be added, not selectively removed — to change the list you must redeclare the entire directive.\r\n\r\n---\r\n\r\n## Usage\r\n\r\n### Podman\r\n\r\n```bash\r\n# nginx\r\npodman pull ghcr.io/kpirnie/nginx:latest\r\n\r\n# PHP 8.4\r\npodman pull ghcr.io/kpirnie/php:8.4-latest\r\n```\r\n\r\n### Example Compose (Podman / Docker)\r\n\r\n```yaml\r\nservices:\r\n  nginx:\r\n    image: ghcr.io/kpirnie/nginx:latest\r\n    ports:\r\n      - \"80:80\"\r\n      - \"443:443\"\r\n      - \"443:443/udp\"   # QUIC/HTTP3\r\n    volumes:\r\n      - ./nginx/conf.d:/etc/nginx/conf.d:ro\r\n      - ./nginx/ssl:/etc/nginx/ssl:ro\r\n      - ./geoip:/etc/nginx/geoip:ro\r\n      - nginx_logs:/var/log/nginx\r\n\r\n  php:\r\n    image: ghcr.io/kpirnie/php:8.4-latest\r\n    volumes:\r\n      - ./app:/var/www/html:ro\r\n      - ./php/conf.d:/usr/local/etc/php/conf.d:ro\r\n      - php_logs:/var/log/php-fpm\r\n\r\nvolumes:\r\n  nginx_logs:\r\n  php_logs:\r\n```\r\n\r\n---\r\n\r\n## Building Locally\r\n\r\nRequires Podman with Buildah. Builds the native platform only (no emulation).\r\n\r\n```bash\r\nchmod +x build-local.sh\r\n./build-local.sh\r\n```\r\n\r\nThe script builds every image, runs smoke tests on each, and prints a summary. Local images are tagged `:local` and are not pushed anywhere.\r\n\r\n**To clean up local images after testing:**\r\n```bash\r\npodman images | grep ':local' | awk '{print $3}' | xargs podman rmi\r\n```\r\n\r\n---\r\n\r\n## Nightly Builds\r\n\r\nBoth images are rebuilt automatically at **02:00 UTC daily** via GitHub Actions. Builds are also triggered on any push to `main` that touches the relevant image directory.\r\n\r\nThe PHP workflow builds all four versions in parallel. If one version fails (e.g. a PECL extension not yet compatible with a new PHP release), the others complete normally.\r\n\r\n---\r\n\r\n## GeoIP2 Databases\r\n\r\nThe nginx image includes the GeoIP2 module but **does not bundle MaxMind databases** — MaxMind requires a free license key and the databases must be kept up to date independently.\r\n\r\n1. Sign up at [maxmind.com](https://www.maxmind.com)\r\n2. Download `GeoLite2-City.mmdb` and/or `GeoLite2-Country.mmdb`\r\n3. Mount them into the container and reference the path in your nginx config:\r\n\r\n```nginx\r\ngeoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {\r\n    $geoip2_country_code country iso_code;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Security Notes\r\n\r\n- Both images use multi-stage builds; no build toolchain is present in the final image.\r\n- nginx is compiled against OpenSSL 4.x (statically linked, native QUIC support) — no OpenSSL runtime dependency.\r\n- All setuid/setgid bits are stripped from the final image filesystem.\r\n- nginx workers and PHP-FPM workers run as unprivileged users (`nginx` UID 101, `www-data` UID 82).\r\n- `server_tokens off` is set globally in nginx.\r\n- `expose_php = Off` is set in `php.ini`.\r\n- SSI and empty_gif modules are disabled in nginx.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT — see [LICENSE](./LICENSE)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpirnie%2Finfra-build","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkpirnie%2Finfra-build","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpirnie%2Finfra-build/lists"}