# phpdocker.io: generating a Docker environment for a PHP application

> phpdocker.io is a web-based generator that produces Docker and Docker Compose environments for PHP applications, and this repository holds both the generator and the site that runs it. The project is a Symfony 8 application on PHP 8.5, it eats its own dog food by running inside an environment it generated, and its Makefile downloads and checksum-verifies the tools it needs.

**phpdocker-io/phpdocker.io** — PHPDocker.io website and environment (php and docker based) generator

- Repository: https://github.com/phpdocker-io/phpdocker.io
- Website: http://phpdocker.io
- Stars: 909 · Forks: 99
- Language: PHP
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/phpdocker-io-phpdocker-io

## A generator that produces the compose file you will live with

phpdocker.io is a web-based generator for Docker environments tailored for PHP applications, and the repository contains both the website and the generator core, open sourced under the Apache 2.0 license.

The output is a set of files: a docker-compose.yaml, a Dockerfile, an nginx configuration and a PHP configuration override, plus the directory layout to hold them. The header of the compose file in this repository makes the origin explicit, with a comment reading Generated on phpdocker.io, which is exactly what a generated artefact should carry.

The stack is worth stating because it bounds what you get. The application runs on PHP 8.5 with Symfony 8.0 as the framework, Yarn and Symfony Asset for the frontend, Redis for storage, and Docker with Docker Compose for the environment.

The design goal of the tool is that the generated environment is meant to be edited and owned rather than regenerated forever. That is the same reasoning behind most scaffolding tools, and it is the right default for PHP, where the shape of a working environment is well known and mostly mechanical: nginx in front, PHP-FPM behind it, a volume mapping the application directory, Composer and Yarn caches mounted so installs are fast, and a database service if the application needs one.

What the generator gives you that a hand-written compose file would not is the boring correctness: matching PHP-FPM configuration with the nginx fastcgi settings, the right volume layout for the caches, and a development container that differs from the production one in the ways that matter.

## make init sets up SSL, hosts entry, dependencies and the stack

The prerequisites are Docker with Docker Compose, mkcert for local SSL, and make. The recommended installation is a single target.

```bash
make init
```

The documentation lists what that command performs. It cleans up caches, provisions a local SSL certificate using mkcert, creates a hosts entry for phpdocker.local, installs PHP dependencies via Composer, installs JS dependencies via Yarn, installs web assets, and starts up the Docker environment.

Once it finishes, the application is available at https://phpdocker.local:10000, and you can head directly to the /generator route.

Two details are worth calling out. The local SSL certificate matters because the site is served over HTTPS on a hostname that does not resolve publicly, so mkcert generates a certificate your browser will accept rather than a self-signed one you have to click through every time. And the hosts entry is created for you, so phpdocker.local resolves to the local machine without editing /etc/hosts by hand.

The other commands cover the daily loop. make start and make stop manage the environment, make shell opens a bash shell inside the PHP container, make static-analysis runs PHPStan at level 9, and make unit-tests runs PHPUnit.

The container is run with a coverage-friendly PHP setting for the test path.

```bash
PHP_RUN=docker compose run -e XDEBUG_MODE=coverage --rm php-fpm
```

That XDEBUG_MODE=coverage flag is the detail that makes coverage work in a container, since Xdebug is only slow and enabled when it needs to be.

For Windows the documentation points at WSL2 with Docker Desktop's WSL 2 backend, and the app is then at https://localhost:10000 instead, because the hosts file entry is a Unix mechanism.

## The generated compose file has three services and mounts your source

The compose file in the repository is the clearest example of what the generator produces, and it is worth reading line by line.

```yaml
services:
  redis:
    image: redis:alpine

  webserver:
    image: nginx:alpine
    working_dir: /application
    volumes:
      - .:/application
      - ./infrastructure/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./infrastructure/local/:/etc/ssl/local/
    ports:
      - "20000:443"

  php-fpm:
    build:
      dockerfile: Dockerfile
      target: backend-dev
      context: .
```

Three services: Redis for state, nginx as the HTTP front, and PHP-FPM built from the local Dockerfile rather than pulled as an image. The PHP service builds with target backend-dev, which selects the development stage of the multi-stage Dockerfile.

The source directory is bind mounted into both nginx and PHP-FPM as /application, so your code is on the host and inside the containers at the same path. That is what makes the development loop work without rebuilding an image on every edit. The nginx configuration and the local SSL directory are mounted as individual files rather than copied into the image, which means you can adjust the web server config and regenerate the certificate without a rebuild.

The ports line maps 20000 to 443, so nginx listens on 443 inside the container and the host reaches it on 20000.

Two smaller points. The Composer cache is mounted from the host into the container, at ~/.cache/composer to /root/.composer/cache, so dependency installs do not re-download on every container start. And the php-ini-overrides.ini file is mounted into the FPM configuration directory, which is the mechanism for changing PHP settings without rebuilding the image.

## One Dockerfile, three targets, and the split between dev and deployment

The Dockerfile is built around the PHP version as a build argument, defaulting to 8.5.

```dockerfile
ARG PHP_VERSION=8.5

FROM phpdockerio/php:${PHP_VERSION}-fpm AS backend-base
WORKDIR "/application"

RUN apt-get update \
    && apt-get -y --no-install-recommends install \
        php${PHP_VERSION}-intl \
        php${PHP_VERSION}-redis \
```

The base image comes from the phpdockerio/php family, so the project builds on its own published images rather than starting from a generic PHP image. Extensions are installed with the version interpolated into the package name, which is why the ARG is repeated inside each stage: a build argument declared before the first FROM is not automatically available after it.

There are three targets. backend-base is the shared layer with the intl and redis extensions. backend-dev adds xdebug and enables it with phpdismod xdebug, and it is the target the compose file selects. backend-deployment is the production stage, which sets APP_ENV and SYMFONY_ENV to prod, installs Composer dependencies with --no-dev --no-scripts, copies the configuration, source, templates and the front controller, dumps an optimized autoloader with --classmap-authoritative, warms the Symfony cache, and hands the var/ directory to www-data.

The deployment stage also copies three separate configuration files into the FPM configuration directory: the ini overrides, a production opcache configuration, and an optimised FPM pool configuration. Each gets a z- prefix so it sorts after the distribution's own files.

The frontend part of the Dockerfile has a comment explaining a decision worth repeating: use the normal Node image rather than Alpine for more consistent ARM support. That is the kind of note that only appears after someone hit the problem, and it is the sort of detail that a generated environment for a developer on an ARM Mac will otherwise rediscover the hard way.

## Checksum-verified downloads in the Makefile

The Makefile in this repository does something that most Makefiles do not, and it is worth looking at closely because it is the clearest signal of the project's engineering standards.

The tools it needs are downloaded from upstream releases: mkcert v1.4.3 and hosts 3.6.4, each with a pinned SHA-256. The comment explains the situation in detail. Upstream publishes no checksum for the mkcert v1.4.3 release assets, since the GitHub release API reports a digest of null for each of them, so the SHA-256 values were computed locally from the assets downloaded over HTTPS from the official release URLs on 2026-09-25.

The Windows value is independently corroborated by the mkcert 1.4.3 Chocolatey package, which contains the same bytes. The hosts tool is fetched from the commit that the 3.6.4 tag points at, 9a929dc70fa11bfe6dc5b0f1d53aea442395edd3, and was hashed on the same date. The comment closes with an instruction: if an asset is ever republished, update its URL and its hash in the same commit.

That is a careful piece of supply chain handling for a development tool. The SHA256_CMD variable even picks between sha256sum on Linux and shasum on macOS so the verification works on both.

The rest of the Makefile uses a similar pattern. BUILD_TAG is composed from the current date and the short git hash, and INFECTION_THREADS defaults to 8, which points at mutation testing as part of the test tooling.

The repository also carries phpstan.neon with level 9 enforcement, a phpunit.xml.dist, a .clang-format-free but otherwise conventional Symfony layout with src/, config/, templates/, public/, tests/ and assets/, a symfony.lock, and an AGENTS.md referenced from the contributing notes as a detailed guide for developers and AI agents.

## Licence, maintenance and what the frontend stack tells you

The documentation states the project is open sourced under the Apache 2.0 license, with the LICENSE file at the repository root. The licence field on the repository itself says Other, a custom licence GitHub cannot classify, which is a metadata artefact rather than a disagreement with the documentation. This is not legal advice, but Apache 2.0 is one of the more permissive and more widely accepted licences for a tool that generates code people then own, and the generated output is yours to modify.

The last push was on 2026-09-25, and there are no GitHub releases, so the versioning lives in the repository rather than in tags. There is a CHANGELOG-style history in the repository only insofar as the file list shows it.

The frontend stack is smaller than the PHP side, which is worth noting because it tells you what kind of site this is. The package.json has one runtime dependency, font-awesome at 4.7.0, and dev dependencies on autoprefixer, postcss, postcss-cli and tailwindcss at 3.4.19, with a single build:css script that runs postcss over assets/css/app.css. The engines field requires npm 9.0.0 or newer. There is a postcss.config.js and a tailwind.config.js at the root.

So this is a server rendered Symfony application with a utility CSS layer built by Tailwind and PostCSS, plus the Font Awesome icon set. That is a deliberately minimal frontend: no bundler for JavaScript, no framework, no client side application. For a generator whose output is a set of configuration files, that is a sensible amount of front end and it keeps the attack surface of a public tool small.

The practical implication for someone evaluating the generator is that the frontend you get in the generated environment is whatever the generator decides, not whatever you would have chosen. If your application has a specific asset pipeline, the generated environment is a starting point you adapt rather than a finished setup.

## Conclusion

phpdocker.io suits a developer starting a new PHP application who wants a working Docker environment with nginx, PHP-FPM, Composer and Node in a few clicks rather than assembling one, and the generated compose file is a readable artefact you can edit afterwards. It does not suit an existing application with a settled build, since the value is in generating a starting point rather than replacing a working setup, and the generator does not cover the operational side of running containers in production. Before adopting it, read the generated docker-compose.yaml and Dockerfile as the real deliverable, since those files are what you will own, and check the PHP version and extensions in the generated image against what your application needs.

## FAQ

### What does phpdocker.io generate?

A Docker and Docker Compose environment for a PHP application: a docker-compose.yaml with services for nginx, PHP-FPM and Redis, a Dockerfile with development and deployment targets, plus nginx and PHP-FPM configuration files. The repository's own compose file carries a header noting it was generated on phpdocker.io.

### How do I set up phpdocker.io locally?

You need Docker with Docker Compose, mkcert and make, then run make init. That cleans caches, provisions a local SSL certificate with mkcert, creates a hosts entry for phpdocker.local, installs Composer and Yarn dependencies, installs web assets and starts the Docker environment, after which the app is at https://phpdocker.local:10000.

### What commands does the phpdocker.io Makefile provide?

make init for the initial setup, make start and make stop to manage the environment, make shell for a bash shell inside the PHP container, make static-analysis to run PHPStan at level 9, and make unit-tests to run PHPUnit.

### What tech stack does phpdocker.io use?

PHP 8.5 with Symfony 8.0, Yarn and Symfony Asset for the frontend, Redis for storage, and Docker with Docker Compose for the environment. The frontend build uses Tailwind CSS 3.4.19 with PostCSS and Font Awesome.

### Can I run phpdocker.io on Windows?

Yes, using WSL2 with Docker, following the Docker Desktop WSL 2 backend documentation. In that setup the application is available at https://localhost:10000 rather than the phpdocker.local hostname used on Unix hosts.

## Sources

- [Issues](https://github.com/phpdocker-io/phpdocker.io/issues)
- [phpdocker-io/phpdocker.io on GitHub](https://github.com/phpdocker-io/phpdocker.io)
- [Project website](http://phpdocker.io)
- [README](https://github.com/phpdocker-io/phpdocker.io/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/phpdocker-io-phpdocker-io
