iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers VPS plans with one-click Docker, NVMe storage and a 14-day free trial.
The most maintainable local setup is a multi-stage PHP image plus a Compose file that runs the application and database together. Use php:8.2-apache for the shortest path on a small project; use PHP-FPM behind Nginx or a separate Apache service when you need a production-like web tier. Keep source code editable through a bind mount or Compose Watch, connect to the database by its Compose service name, and store database files in a named volume.
What Dockerfile and Docker Compose each do
A PHP development environment normally has four concerns: the PHP runtime, PHP extensions, application dependencies, and supporting services such as a database. Split those responsibilities deliberately.
Free tools Windows power users keep installed
One-click scans. No signup required.
| File or component | Responsibility | Typical development choice |
|---|---|---|
Dockerfile |
Builds the PHP image, installs extensions, copies application files, and defines development or production targets. | Multi-stage build with separate Composer dependency stages and a development target. |
compose.yaml |
Defines running services, ports, environment variables, dependencies, mounts, networks, health checks, and volumes. | server and db services on one private Compose network. |
| Bind mount or Compose Watch | Makes host edits visible without rebuilding the image for every PHP change. | Mount ./src into the application document root, or declare a Watch sync rule. |
| Named volume | Stores database files outside the disposable database container. | db-data:/var/lib/mysql for a MariaDB service. |
Compose’s model is intentionally declarative: the YAML file describes the services, and the Compose CLI creates and starts them as one application. The resulting network gives each service a DNS name equal to its service key, so application code uses db rather than localhost when connecting to the database.
Choose the web-serving architecture first
PHP with Apache
The php:*-apache images are the shortest route for a small application. PHP and Apache run in one container, so there is less proxy configuration and only one application service to start. The example in this guide uses php:8.2-apache, enables Apache’s rewrite module, and publishes container port 80 on host port 9000.
#1 Best Overall
- Powerful Turbo Fan:CAGIWIRU Electric Air Duster have reached speed up to 130000RPM,Can efficient remove dust and debris.With LED light and Three Adjustable Speed to meet with different cleaning tasks.
- With Attached USB C Fasting Charging cable.Charing 3 hours,Enjoy up to 240 minutes of use on the lowest setting, For third speed can use for 25mins.with four charging options to meet with your needs.
- Wide Application: attached 5 different nozzles,making it suitable for a variety of scenes, such as car,pet, pc, keyboards, and other electronic devices. It also serves for office and home clean duster.
- Handy Design & Portable:Cordless air duster electric nozzle is Light weight and small size, design and comfortable to hold and space saving, convenient to carry around. You can put it in your room or inside the car without taking up space.
- Economical &Reusable: Built durable materials with a long-lasting battery,our CAGIWIRU air duster it's a sustainable alternative to disposable air cans.
This option couples PHP request handling to Apache. That is convenient locally, but it does not model an architecture in which a dedicated reverse proxy, TLS terminator, or static-file tier sits in front of PHP.
PHP-FPM behind Nginx or Apache
PHP-FPM is the better fit when the web server should be a separate service or when local development should resemble a multi-container production stack. FPM listens for FastCGI requests; it does not serve HTTP pages by itself. An Nginx or Apache container must mount the application source and forward PHP requests to the FPM service over the private Compose network.
Expose the proxy’s HTTP port to the host, not FPM’s internal port. Keep FPM off the host network and do not publish its listening port unless a specific diagnostic task requires it.
| Choice | Use it when | Trade-offs |
|---|---|---|
php:*-apache |
A small app needs the simplest local setup. | Fewer services and less configuration, but PHP serving is coupled to Apache. |
| PHP-FPM plus Nginx or Apache | You want a separate web tier or a layout close to many production stacks. | More services, shared source and configuration, and a required reverse-proxy connection. |
Prerequisites and a sensible project layout
Install Docker Desktop, or Docker Engine with the Compose plugin, on the host. Verify both commands before creating files:
docker version
docker compose version
Create a project directory and use a layout that keeps application code, container configuration, and tests easy to identify:
php-compose-app/
├── compose.yaml
├── Dockerfile
├── composer.json
├── composer.lock
├── docker/
│ └── php/
│ └── conf.d/
│ └── development.ini
├── src/
│ ├── index.php
│ └── hello.php
└── tests/
Commit composer.json and composer.lock. The lock file makes the development image repeatable and prevents an unrelated dependency release from changing an existing build.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Build a multi-stage PHP image
Multi-stage builds let development and production use the same base runtime while receiving different Composer dependency sets and PHP settings. Development can include PHPUnit and other packages from require-dev; production can install only runtime dependencies. Keeping those stages separate also avoids putting development-only configuration into a production image.
Example Dockerfile
FROM composer:lts AS composer-dev
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --prefer-dist --no-interaction --no-progress
FROM composer:lts AS composer-prod
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --no-progress --classmap-authoritative
FROM php:8.2-apache AS runtime
WORKDIR /var/www/html
RUN docker-php-ext-install pdo pdo_mysql
&& a2enmod rewrite
COPY --from=composer-dev /usr/bin/composer /usr/bin/composer
FROM runtime AS development
COPY --from=composer-dev /app/vendor ./vendor
COPY src/ ./
COPY docker/php/conf.d/development.ini /usr/local/etc/php/conf.d/development.ini
FROM runtime AS production
COPY --from=composer-prod /app/vendor ./vendor
COPY src/ ./
The pdo and pdo_mysql extensions cover a MySQL-compatible database such as MariaDB. Add other extensions with docker-php-ext-install only when the application requires them; every extra extension increases build time and the runtime surface.
The composer:lts stage supplies Composer without requiring a separate host installation. Copying the Composer executable into the runtime image lets you run dependency commands through docker compose exec. If your application needs a different PHP minor version, change the PHP base image and verify that every dependency supports it.
Rank #2
- Screen Cleaner is Your Screen’s New Bestfriend! - EVEO is proud to present a screen cleaner that’s perfectly suited for your TV. Meet your new screen cleaner, computer screen cleaner, laptop cleaner, iPad cleaner, Macbook,tv cleaner or any other electronic device. Our screen cleaner spray and wipe along with the included plush microfiber cloth, easily removes dust, fingerprints & other smudges on the screen’s surface. Clean effectively for a streak-free screen
- Gentle on your Screens - Our screen cleaner spray and wipes is compatible to be used even for the most sensitive LCD, LED, CRT, and OLED screens in proper use. Be at ease knowing that this product is compatible with all the major brands: LG, SONY, Samsung, Sharp, iPad, iPhone, Android screens, Kindle, PC monitors - we’ve got you covered! Can be used as LCD screen cleaner, tv cleaner, smart tv screen cleaner, and more
- Premium Super Soft Plush Microfiber Cloth - Included in this efficient screen cleaning kit. This screen cleaner spray and wipe has our signature plush microfiber cloth specially designed to comply with screens and monitors and leave them streak-free. Has been shown to effectively absorbs stubborn stains, zero streaks, and no fiber shedding. Always use this together with our screen cleaner for the best results
- Easy-to-Use Screen Cleaner Kit + microfiber cleaning cloth - The EVEO Screen Cleaning Kit will easily be your new favorite tool when it comes to taking care of the things you love most, quick and easy solution for a streak-free screen. Simply screw on the sprayer gun on the screen cleaner bottle. Spray the cleaning liquid onto the microfiber cloth, then wipe the screen until desired cleanliness & shine is achieved.
- Quality Screen Care Kit Worth Purchasing - Enjoy a convenient Screen Cleaner anytime, anywhere you need it. Your satisfaction is our top priority. We stand behind the quality of our products and that’s why, you shouldn’t be worrying at all. We manufacture the Best Screen Cleaner. and can be used for laptop cleaning kit, laptop screen cleaner, tv screen cleaner for smart tv. MacBook screen cleaner, monitor cleaner or screen cleaner spray for electronic devices with microfiber cleaning cloth
Image tags can move. For a reproducible build, pin a tested tag and, where your release process requires it, pin the image by digest. A digest identifies one immutable image, whereas a tag may later point to a different build.
Development PHP settings
Create docker/php/conf.d/development.ini with settings that make local errors visible:
display_errors=On
display_startup_errors=On
error_reporting=E_ALL
log_errors=On
memory_limit=512M
Do not copy this file into the production target unless exposing detailed errors is an intentional, protected operational choice.
Composer manifest and PHPUnit
A minimal manifest can put framework and test packages in the appropriate sections:
{
"require": {
"php": "^8.2"
},
"require-dev": {
"phpunit/phpunit": "^10"
},
"autoload": {
"psr-4": {
"App\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\": "tests/"
}
}
}
Run composer update once in a controlled environment to create the lock file, then let image builds use composer install. For an existing project, retain its current PHP and PHPUnit constraints rather than copying these example versions blindly.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsDefine the application and database services
Compose file for Apache, MariaDB, and persistent data
The following file builds the development target, publishes Apache on port 9000, waits for a healthy database, and stores MariaDB data in a named volume.
services:
server:
build:
context: .
dockerfile: Dockerfile
target: development
ports:
- '9000:80'
volumes:
- ./src:/var/www/html
environment:
APP_ENV: development
DB_HOST: db
DB_PORT: '3306'
DB_DATABASE: app
DB_USERNAME: app
DB_PASSWORD: app_password
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: sync
path: ./src
target: /var/www/html
- action: rebuild
path: composer.json
- action: rebuild
path: composer.lock
db:
image: mariadb:11
environment:
MARIADB_DATABASE: app
MARIADB_USER: app
MARIADB_PASSWORD: app_password
MARIADB_ROOT_PASSWORD: change_this_root_password
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ['CMD', 'mariadb-admin', 'ping', '-h', 'localhost', '-u', 'root', '-pchange_this_root_password']
interval: 5s
timeout: 5s
retries: 10
volumes:
db-data:
The credentials above are suitable only for an isolated local project. Put real development credentials in an uncommitted .env file or use your team’s secret-management approach; never commit production passwords to Compose YAML.
The develop.watch block requires a current Compose implementation. If your installed Compose version does not support Watch, remove that block and keep the bind mount. Do not use both mechanisms to sync the same path unless you understand which one wins.
Why the database uses a separate service
The database has its own lifecycle, image, health check, and storage. Recreating the PHP container therefore does not recreate the database. On the default Compose network, the hostname is db, the service key. The host-published port is unnecessary for application traffic; it exists only if you deliberately need a database client on the host.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #3
- 【Large size】:Measuring 800 x 300 mm/ 31. 5×12 inches, which is wider and taller than others. Large mouse pad can perfectly fit your keyboard and mouse of any size, and there is also ample space for peripherals such as phones, tablets, etc.
- 【LED backlight mouse pad】:Blade Hawks RGB mouse pad provides 7 static modes and 7 dynamic modes will give you the options you are looking for. Power-off memory function remembers the last lighting mode you selected. The super glow fiber Chroma will give you a colorful gaming setup that you want.
- 【Anti-slip rubber base】:Made from Natural rubber, this gaming mousepad will firmly grip your desktop, allowing you to enjoy the ultimate precision in the games you are most passionate about. It also comes with a texture that enhances this ability even further.
- 【Smooth and waterproof surface】: Micro-textured cloth surface, for extremely precise mouse control and a smooth movement. When liquid splashes on the gaming mouse pad, it forms water droplets and slides down. Will not delay your work or gameplay.
- 【Plug and play】: This mouse and keyboard pad is powered by USB, plug and play, no driver required. One button control light modes. Press one time to change the lighting mode, press twice to change the brightness, press and hold about 3 seconds turn ON/OFF.
The named volume survives container removal. It remains until you explicitly remove it, so plan cleanup and backups rather than assuming docker compose down deletes data.
Start the stack and verify the result
- Build the selected target and start every service:
docker compose up --build - In another terminal, confirm service state:
docker compose psThe database should report healthy before the application attempts database-dependent startup work.
- Open
http://localhost:9000/hello.phpfor the sample page, or usehttp://localhost:9000/when your entry point isindex.php. - Follow application output when a request fails:
docker compose logs -f server docker compose logs -f db
Keep the foreground up process running while developing. Use docker compose up -d --build when you want detached services, and stop them with docker compose down. Stopping containers does not remove the named database volume.
Make source changes visible without rebuilding
Bind mounts
The ./src:/var/www/html mount replaces the image’s copied application directory with the host directory. Editing a PHP file on the host is therefore visible to Apache immediately. This is familiar and works with older Compose versions, but filesystem performance and UID/GID permissions vary between operating systems and Docker Desktop file-sharing modes.
A bind mount is a development convenience, not a production image strategy. A production target should contain the application code in the image and should not depend on a developer’s host directory.
Recommended Free Tools
Compose Watch
Watch rules declare what should be synchronized and what should trigger a rebuild. Sync source files for fast edits; rebuild when composer.json, composer.lock, the Dockerfile, or extension configuration changes. Start Watch with:
docker compose watch
Use one approach consistently for each path, and exclude generated directories such as vendor or test reports when they would create an unnecessary sync loop.
Run Composer and PHPUnit inside containers
Running tools in the development container guarantees that PHP, extensions, and dependency versions match the application runtime.
Install or update dependencies
docker compose exec server composer install
docker compose exec server composer dump-autoload
docker compose exec server composer update vendor/package
Use composer install for normal work because it honors the lock file. Use composer update only when intentionally changing dependency versions, then review and commit the resulting lock-file changes.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Run PHPUnit
docker compose exec server vendor/bin/phpunit
If the service is not running, use a one-off container instead:
Rank #4
- 【Softer and More Comfortable】Vaydeer wrist rest has unique diamond pattern, which is the combination of softness and aesthetics. The materials of wrist rest are improved into higher quality memory foam and covered with silky smooth lycra. The computer wrist rest makes you as comfortable and cushiony as like rest your wrists on clouds.
- 【Ergonomic Wrist Saver】The wrist rests for keyboard and mouse comes with a 17.32×3.15×0.83 inch keyboard wrist pad and a 5.94×3.15×0.83 inch mouse wrist support. Based on ergonomic design, the unique concave shape is the perfect fit for your wrist joints. The wrist rest pad fits most computer keyboards and laptops, improve hand and wrist posture, release your wrist and arm stress.
- 【Non-Slip Rubber Bottom】Featuring an anti-skid silicone base on the bottom, this wrist keyboard support stays firmly in place on your desk, preventing the padding from sliding around, ensuring stable and consistent wrist support during extended computer sessions.
- 【Better Experience & Pain Relief】Our keyboard arm rest is beneficial to alleviate the soreness caused by direct contact and friction between your arm and a hard desk surface, reducing the risk of wrist fatigue or carpal tunnel. The soft texture of memory foam can evenly distribute the pressure around your wrists and provide good support with just enough give.
- 【Helpful in Multiple Scenarios】Whether you're working, studying, writing, typing, gaming, this keyboard and mouse rest combo is an essential accessory to add comfort and support to your hands and wrists. It’s also a great gift for men, women, family, friend, coworker, gamer, teacher, etc.
docker compose run --rm server vendor/bin/phpunit
The Docker PHP guide’s sample test run reports “1 test, 1 assertion”; that is the result of its example test, not a performance or quality statistic for PHP applications generally. Add a PHPUnit configuration and test bootstrap that match your own project before relying on that command in continuous integration.
Execute PHP and database commands
docker compose exec server php -v
docker compose exec server php -m
docker compose exec db mariadb -uapp -papp_password app
Inside the server container, localhost refers to the server container itself. Use DB_HOST=db for MariaDB connections. Inside the database container, the database listens on its container port, normally 3306, regardless of whether you publish that port to the host.
Connect PHP to MariaDB correctly
Read connection settings from environment variables rather than hard-coding them in source. A PDO connection can be assembled from the Compose values:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall<?php
$host = getenv('DB_HOST') ?: 'db';
$port = getenv('DB_PORT') ?: '3306';
$name = getenv('DB_DATABASE') ?: 'app';
$user = getenv('DB_USERNAME') ?: 'app';
$password = getenv('DB_PASSWORD') ?: 'app_password';
$pdo = new PDO(
"mysql:host={$host};port={$port};dbname={$name};charset=utf8mb4",
$user,
$password,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
Compose starts services in dependency order, but a started container is not necessarily ready to accept connections. The database health check and the service_healthy condition reduce that race during startup. Applications should still handle transient connection failures gracefully, especially when a database is upgraded or restored.
Keep development and production targets separate
Build the production target explicitly when you need an image that contains only production dependencies:
docker build --target production -t php-compose-app:production .
For local work, Compose selects target: development. The development target includes packages from require-dev, a verbose PHP configuration, Composer, and a source mount. The production target uses --no-dev, authoritative Composer autoloading, copied source, and no host bind mount.
- Pin PHP and database image versions, and use digests when your release process requires immutable inputs.
- Do not publish FPM or database ports just to make internal services convenient to reach.
- Use a non-production password set for local development and inject production secrets through the deployment platform.
- Run the same PHPUnit suite in a disposable test stage or CI job before releasing the production image.
- Review the final image for development files, test fixtures, credentials, and writable directories that should not ship.
Back up and clean up the database volume
A named volume is durable, not automatically backed up. Create a logical dump before destructive migrations or environment resets:
docker compose exec db mariadb-dump -uapp -papp_password app > backup.sql
Restore into a running database with:
cat backup.sql | docker compose exec -T db mariadb -uapp -papp_password app
To remove containers and the network while preserving data, run docker compose down. To intentionally delete the database volume as well, use:
docker compose down -v
That command is irreversible for the local volume unless you have a dump. Treat it as a deliberate reset, not routine cleanup.
Best Value
- 【No Drill Mounted】Cable management tray can be assembled in as little as 3 minutes or less-simply. Side clips easily clampe the edge of desk, pass through the holes on the sides, and secure excess cables with baskets. Desk cable management supports inward or outward installation to meet your needs in different scenarios, and provides you with great convenience when collecting and organizing wires.
- 【Rubber Pad Protects Desk】Wire organizer under desk built-in soft rubber pads on the clamp protect your desk from scratches, so you will not damage your furniture or office.
- 【Sturdy and Beautiful】Cord management under desk is made of high-quality carbon steel and holds about 15 lbs. The wire basket has 2 holes on each side of the tray to enable your cables to pass freely. Cable baskets are suitable for desk thickness use from 0.4" to 2.4".
- 【Space Saving & Keep Organized】Desk cable management tray holds all your power cords, outlet strips, USB hubs and computer transformers hidden from your feet, and the data cables will not enter the vacuum cleaner to keep your desk clean and tidy. Desk wire organizer is also suitable for use in the kitchen and outdoors.
- 【Reduce Safety Risks】Cable tray under desk keeps all plugs away from your kids, no more worry about tripping. Using 2 holes wire tray saves extra space, helps you easily cabling and wire protection. The mesh design is not easy to accumulate dust, but avoids safety accidents caused by messy wires.
Troubleshoot the failures that appear most often
The host port is already in use
If port 9000 is occupied, change only the host side of the mapping, for example '9001:80', then open http://localhost:9001/. The container continues to listen on port 80.
The application cannot reach MariaDB
- Confirm that the hostname is
db, notlocalhost. - Run
docker compose psand wait for the database health status. - Inspect
docker compose logs dbfor invalid credentials, initialization errors, or an existing volume created with different credentials. - Verify that
pdo_mysqlappears indocker compose exec server php -m.
Changes are not visible
Check that the host path is correct and that the mount target matches Apache’s document root. A bind mount hides files copied into that directory during the image build, so a missing host file can make a correctly built image appear empty. If using Watch, verify that your Compose version supports develop.watch and that the rule’s source and target paths are correct.
Composer reports missing packages
Rebuild after changing the manifest or lock file: docker compose up --build. If a bind mount hides vendor, either mount only the source directory as shown here or run Composer inside the container after the mount is active. Do not copy a host machine’s vendor directory into an image built for a different operating system or PHP extension set.
Permission errors occur on generated files
Container users and host users can have different numeric IDs. Identify the process user with docker compose exec server id, then adjust the development image, mount permissions, or generated-directory ownership according to your host platform. Avoid making the entire application tree world-writable as a shortcut.
Apache returns 403 or rewrite rules do not work
Confirm that the application has an index file, the mounted directory is readable, and a2enmod rewrite ran during the image build. If the application relies on .htaccess, Apache’s directory configuration must permit overrides; add that configuration deliberately rather than weakening unrelated directories.
When the FPM layout is worth the extra services
Choose FPM when you need independent scaling, a dedicated static-file and TLS layer, or parity with a deployment architecture that already uses Nginx or Apache as a reverse proxy. The Compose design then has at least three concerns: an Nginx service publishing port 80, a PHP-FPM service built from a PHP-FPM base image, and the database. Mount the same application source into the proxy and FPM containers, configure Nginx’s document root, and send requests for .php files to the FPM service name on the private network.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →This layout introduces more failure points: mismatched document roots, incorrect FastCGI parameters, and source mounts that differ between containers. For a small application with no separate web-tier requirement, Apache is usually the faster route to a working environment.
A repeatable daily workflow
- Change PHP code under
src/; the bind mount or Watch sync updates the running container. - Change dependencies in
composer.json, run Composer in the container, and rebuild so the image records the new lock file. - Run
docker compose exec server vendor/bin/phpunitbefore committing. - Inspect service health with
docker compose psand logs withdocker compose logswhen startup or database behavior changes. - Use a named-volume dump before migrations that could destroy local data.
- Build the
productiontarget separately to confirm that it does not depend on development mounts or packages.
This separation gives you fast editing, a reproducible PHP runtime, a database that survives container replacement, and a clear path from local tests to a leaner production image.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

