PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
146: Deploying PHP Applications with Docker
I see this happen all the time when developers first move toward containerization: they treat a Docker container like a lightweight virtual machine that they "log into" and configure manually. They'll spin up a generic PHP image, bash into it, install a few extensions, and then wonder why their deployment fails the moment they try to scale to a second server or push to a staging environment.
"Docker is just a place to run my code" vs. "The image is the artifact"
The biggest misconception is thinking that the container is just a wrapper for your code. In that mindset, you're likely using volumes to mount your local directory into the container, even in production. While that's great for development because you see changes instantly, it's a nightmare for deployment. If your production server relies on a volume mount, you're not actually deploying a "version" of your app; you're deploying a dependency on a specific file structure existing on a specific disk.
The correct approach is to treat your Docker image as a compiled artifact. Everything the app needs—the PHP runtime, the specific extensions, the composer dependencies, and the source code itself—should be baked into the image. When you push my-app:v1.2.0 to a registry, that image should be able to run on any machine in the world without needing a single file copied over via FTP or SSH.
Handling PHP's quirks with docker-php-ext-install
You can't just run apt-get install php-gd inside the official PHP images. I've seen so many people waste hours on this. The official PHP images use a special set of scripts to ensure extensions are compiled correctly for the specific version of PHP running in the container.
Let's say we're deploying a "Client Portal" app that generates PDF invoices. We need gd for image processing and pdo_mysql for the database. Here is how I would actually write that Dockerfile to make it production-ready:
FROM php:8.2-fpm-alpine
# We need system dependencies first for the PHP extensions to compile
RUN apk add --no-cache \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev
# Use the built-in helper to install and enable PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd pdo_mysql
# Set the working directory
WORKDIR /var/www/html
# Copy the code into the image (the "Artifact" part)
COPY . .
# Install composer dependencies
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer install --no-dev --optimize-autoloader
# Fix permissions for the web server
RUN chown -R www-data:www-data /var/www/html/storage
Notice the COPY . . line. In development, you'd override this with a volume in your compose file, but for deployment, this is what ensures your code is locked into the image version.
Connecting PHP-FPM to a Web Server
Here is another point where people get tripped up: the php:fpm image doesn't actually "serve" web pages. It's a FastCGI Process Manager. It speaks a protocol that browsers don't understand. You need a web server like Nginx to sit in front of it, acting as a translator.
I prefer using a docker-compose.yml file to define this relationship. It keeps the networking simple. You don't need to worry about IP addresses; you just refer to the service by its name.
services:
app:
build: .
restart: always
environment:
- DB_HOST=db
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: portal_db
MYSQL_ROOT_PASSWORD: secret_password
The key here is the web service. It takes the incoming HTTP request and passes it to the app service via port 9000. If you try to run the PHP-FPM container alone and visit port 80, you'll get nothing. It's a common "why isn't it working?" moment that usually just takes a quick look at the architecture to solve.
📋 Practical Task
Containerizing the Invoice-PDF Generator
You have a PHP application that requires the bcmath extension for precise currency calculations and the zip extension for bundling invoices. The application source code is located in the current directory.
Your Task: Create a Dockerfile and a docker-compose.yml file that accomplishes the following:
- Use
php:8.2-fpm-alpineas the base image. - Install the system dependencies required for the
zipextension (hint:libzip-dev). - Use
docker-php-ext-installto install bothbcmathandzip. - Copy the current directory into
/var/www/htmlwithin the image. - Set up a
docker-compose.ymlthat orchestrates two services:app(built from your Dockerfile) andweb(using thenginx:alpineimage), ensuring the web server can communicate with the PHP app.
There are no comments for now.