Dockerizing PHP Application using Docker Init
Tags: docker, php, developer, containers
Published: 2023-12-17 10:12:48

Gone are the days of manually wrangling Dockerfiles and configuration files! Enter `docker init`, your new sheriff in the wild west of containerization. This revolutionary command, available in Docker Desktop 4.19.0+, simplifies Docker life for developers of all skill levels.

Think of `docker init` as your one-stop shop for containerizing applications. Simply point it towards your project folder, and voila! It scans your code, automagically generating the essential Dockerfiles, Compose files, and .dockerignore files needed to run your project in a container. No more tedious configurations, no more syntax headaches - just pure containerized bliss.

## The benefits are manifold:

- Speedy Experimentation: New to Docker? Want to test waters without writing mountains of code? `docker init` is your perfect partner-in-crime. Try out different languages and frameworks in containers with minimal effort.
- Learning Made Easy: Want to master containerization? `docker init` can be your friendly tutor. Its automatic file generation provides a concrete template, perfect for understanding how Docker pieces fit together.
- Seamless Integration: Already rocking Docker for some projects? `docker init` ensures consistent practices across your entire workflow. Standardized asset creation means less troubleshooting and more coding fun.

And the party's not over yet! The current Beta supports Go, Node, and Python, with Java, Rust, and .NET coming soon. Additionally, the Docker team welcomes your feedback - if you have a language or framework you'd love to see supported, just jump through their Google form and make your voice heard.

In a nutshell, `docker init` is a game-changer for developers:

- Simplifies containerization: Get your projects Dockerized in seconds, not hours.
- Lowers the learning curve: Dive into containerization with ease, even as a beginner.
- Standardizes workflow: Say goodbye to inconsistencies and embrace uniformity.


Introduced for the first time in Docker Desktop 4.18, the new `docker init` CLI generates Docker assets for projects, making it easier to create Docker images and containers. When you run the `docker init` command in your project directory, it will guide you through the creation of the necessary files for your project with sensible defaults. These files include:

- .dockerignore
- Dockerfile
- docker-compose.yaml

The `docker init` command also allows you to choose the application platform that your project uses and the relative directory of your main package. 

## Benefits of using `docker init`

The advantages of using the `docker init` command include:

- Simplified Docker asset creation: The command streamlines the creation of necessary Docker files, reducing the chances of errors and ensuring that best practices are followed.
- Saves time and effort: With the default settings and guided prompts, users can quickly create Docker assets without the need for extensive knowledge of Docker or its syntax.
- Better project organization: The generated files provide a standardized and organized structure for the project, making it easier for developers to maintain and update the project over time.
Enhanced portability: By using Docker assets, projects become more portable across different environments, making it easier to move the project from development to production.


## What’s New?

The `docker init` supported Python, Node, Go, ASP.NET 6 and 7 in the older versions. With this new release, the support for PHP with Apache + Composer has been introduced for the first time.

The `docker init` support for PHP and Composer is a welcome addition, and it makes it easier than ever to develop and deploy PHP applications in Docker containers. 


## Prerequisite:

- Download and install Docker Desktop 4.26.0 and above.


To demonstrate the functionality of `docker init`, we will be picking up a simple application from the template using PHP. 

## Getting Started



Let's set up a basic Apache + PHP environment with Composer on macOS. macOS comes with Apache and PHP preinstalled, so you'll mainly need to configure Apache and install Composer.


## Step 1. Enable Apache

Apache is included with macOS, but it's not enabled by default. Open a terminal and run the following commands:


```
sudo apachectl start
```


## Step 2. Enable Apache on System Boot


To have Apache start automatically on system boot, you can run:



```
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
```



You can check if Apache is running by visiting http://localhost in your web browser. You should see the "It works!" default page.


## Step 3. Enable PHP

macOS comes with PHP, but it might not be enabled in Apache by default. Edit the Apache configuration file:



```
sudo nano /etc/apache2/httpd.conf
```



Uncomment the line that includes PHP by removing the # at the beginning of the line:



```
LoadModule php8_module libexec/apache2/libphp8.so
```




## Step 4. Restart Apache

Save the file and restart Apache:


```
sudo apachectl restart
```




## Step 5. Create a test PHP file in your web server's document root:



```
echo "<?php phpinfo(); ?>" | sudo tee /Library/WebServer/Documents/phpinfo.php
```




Open http://localhost/phpinfo.php in your web browser to see the PHP information page.

## Step 6. Install Composer


Run the following command to download and install Composer:


```
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
```


## Step 7. Move the Composer executable to a directory in your PATH:


```
sudo mv composer.phar /usr/local/bin/composer
```


## Step 8. Verify the installation


```
composer --version
```

## Step 9. Create a PHP project with Composer:


Create a new directory for your PHP project:

```
mkdir my-php-project
cd my-php-project
```


## Step 10. Initialize a new Composer project:


```
composer init
```


Follow the prompts to set up your composer.json file.

## Step 11. Create a simple PHP script


In the project directory, create a file named index.php with the following content:


```
<?php
echo "Hello, Composer!";
```


## Step 12. Configure Apache for the project

Create a virtual host configuration for Apache. Create a file named my-php-project.conf in /etc/apache2/other/:


```
<VirtualHost *:80>
    ServerName my-php-project.local
    DocumentRoot /path/to/your/my-php-project
    <Directory /path/to/your/my-php-project>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```



Make sure to replace /path/to/your/my-php-project with the actual path to your project directory.

## Step 13. Enable the virtual host and restart Apache

```
sudo apachectl restart
```



## Step 14. Access your project

Open your web browser and navigate to http://my-php-project.local. You should see the "Hello, Composer!" message.


Let’s use docker init to containerise the whole application.


```
docker init
```

```
Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md


Let's get started!

WARNING: The following Docker files already exist in this directory:
  - .dockerignore
  - Dockerfile
  - compose.yaml
  - README.Docker.md

? Do you want to overwrite them? Yes
? What application platform does your project use?  [Use arrows to move, type to filter]
> PHP with Apache - (detected) suitable for a PHP web application
  Go - suitable for a Go server application
  Python - suitable for a Python server application
  Node - suitable for a Node server application
  Rust - suitable for a Rust server application
  ASP.NET Core - suitable for an ASP.NET Core application
  Other - general purpose starting point for containerizing your application
  Don't see something you need? Let us know!
  Quit
```



It automatically detects the application platform as PHP.


? Do you want to overwrite them? Yes
? What application platform does your project use? PHP with Apache
? What version of PHP do you want to use? 8.3.0
? What's the relative directory (with a leading .) for your app?  [Use arrows to move, type to filter]
  ./ (current directory)
> ./my-php-project
  ./my-php-project/vendor
  ./vendor
  ./vendor/composer
  other




## Step 15. Choose the right project directory path and preferred port



```
? What's the relative directory (with a leading .) for your app? ./my-php-project
? What local port do you want to use to access your server? 9000

CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md

✔ Your Docker files are ready!

Take a moment to review them and tailor them to your application.

If your application requires specific PHP extensions, you can follow the instructions in the Dockerfile to add them.

When you're ready, start your application by running: docker compose up --build

Your application will be available at http://localhost:9000

Consult README.Docker.md for more information about using the generated files.
```



## Content of Dockerfile:


```
# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

################################################################################

# Create a stage for installing app dependencies defined in Composer.
FROM composer:lts as deps

WORKDIR /app

# If your composer.json file defines scripts that run during dependency installation and
# reference your application source files, uncomment the line below to copy all the files
# into this layer.
# COPY . .

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a bind mounts to composer.json and composer.lock to avoid having to copy them
# into this layer.
# Leverage a cache mount to /tmp/cache so that subsequent builds don't have to re-download packages.
RUN --mount=type=bind,source=composer.json,target=composer.json \
    --mount=type=bind,source=composer.lock,target=composer.lock \
    --mount=type=cache,target=/tmp/cache \
    composer install --no-dev --no-interaction

################################################################################

# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the install or build stage where the necessary files are copied
# from the install stage.
#
# The example below uses the PHP Apache image as the foundation for running the app.
# By specifying the "8.3.0-apache" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile.
# If reproducability is important, consider using a specific digest SHA, like
# php@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4.
FROM php:8.3.0-apache as final

# Your PHP application may require additional PHP extensions to be installed
# manually. For detailed instructions for installing extensions can be found, see
# https://github.com/docker-library/docs/tree/master/php#how-to-install-more-php-extensions
# The following code blocks provide examples that you can edit and use.
#
# Add core PHP extensions, see
# https://github.com/docker-library/docs/tree/master/php#php-core-extensions
# This example adds the apt packages for the 'gd' extension's dependencies and then
# installs the 'gd' extension. For additional tips on running apt-get, see
# https://docs.docker.com/go/dockerfile-aptget-best-practices/
# RUN apt-get update && apt-get install -y \
#     libfreetype-dev \
#     libjpeg62-turbo-dev \
#     libpng-dev \
# && rm -rf /var/lib/apt/lists/* \
#     && docker-php-ext-configure gd --with-freetype --with-jpeg \
#     && docker-php-ext-install -j$(nproc) gd
#
# Add PECL extensions, see
# https://github.com/docker-library/docs/tree/master/php#pecl-extensions
# This example adds the 'redis' and 'xdebug' extensions.
# RUN pecl install redis-5.3.7 \
#    && pecl install xdebug-3.2.1 \
#    && docker-php-ext-enable redis xdebug

# Use the default production configuration for PHP runtime arguments, see
# https://github.com/docker-library/docs/tree/master/php#configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Copy the app dependencies from the previous install stage.
COPY --from=deps app/vendor/ /var/www/html/vendor
# Copy the app files from the app directory.
COPY ./my-php-project /var/www/html

# Switch to a non-privileged user (defined in the base image) that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
USER www-data
```



## Output of Docker Compose File


```
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/

# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
    ports:
      - 9000:80

# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker-compose up`.
#     depends_on:
#       db:
#         condition: service_healthy
#   db:
#     image: postgres
#     restart: always
#     user: postgres
#     secrets:
#       - db-password
#     volumes:
#       - db-data:/var/lib/postgresql/data
#     environment:
#       - POSTGRES_DB=example
#       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
#     expose:
#       - 5432
#     healthcheck:
#       test: [ "CMD", "pg_isready" ]
#       interval: 10s
#       timeout: 5s
#       retries: 5
# volumes:
#   db-data:
# secrets:
#   db-password:
#     file: db/password.txt
```


## Step 16. Building and Running the Container

```
docker compose up --build
```

## Results:


```
docker compose up --build
[+] Building 5.7s (18/18) FINISHED                                                                   docker:desktop-linux
 => [server internal] load build definition from Dockerfile                                                          0.0s
 => => transferring dockerfile: 3.74kB                                                                               0.0s
 => [server internal] load .dockerignore                                                                             0.0s
 => => transferring context: 725B                                                                                    0.0s
 => [server] resolve image config for docker.io/docker/dockerfile:1                                                  2.6s
 => [server auth] docker/dockerfile:pull token for registry-1.docker.io                                              0.0s
 => CACHED [server] docker-image://docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab2021  0.0s
 => [server internal] load metadata for docker.io/library/composer:lts                                               2.9s
 => [server internal] load metadata for docker.io/library/php:8.3.0-apache                                           2.9s
 => [server auth] library/php:pull token for registry-1.docker.io                                                    0.0s
 => [server auth] library/composer:pull token for registry-1.docker.io                                               0.0s
 => [server deps 1/3] FROM docker.io/library/composer:lts@sha256:cd1ab955f511b42b377bf95338bcf38765bc7f80f2f3d5014a  0.0s
 => [server internal] load build context                                                                             0.0s
 => => transferring context: 453B                                                                                    0.0s
 => [server final 1/4] FROM docker.io/library/php:8.3.0-apache@sha256:c55d99c94f804ee54177ba00961d2441333b277a67e6a  0.0s
 => CACHED [server final 2/4] RUN mv "/usr/local/etc/php/php.ini-production" "/usr/local/etc/php/php.ini"            0.0s
 => CACHED [server deps 2/3] WORKDIR /app                                                                            0.0s
 => CACHED [server deps 3/3] RUN --mount=type=bind,source=composer.json,target=composer.json     --mount=type=bind,  0.0s
 => CACHED [server final 3/4] COPY --from=deps app/vendor/ /var/www/html/vendor                                      0.0s
 => CACHED [server final 4/4] COPY ./my-php-project /var/www/html                                                    0.0s
 => [server] exporting to image                                                                                      0.0s
 => => exporting layers                                                                                              0.0s
 => => writing image sha256:9f4b8292e9a7c461ec91a587da88c4bcde4c7d4c99a21ea8c885b2e4113e38b2                         0.0s
 => => naming to docker.io/library/demo-app-server                                                                   0.0s
[+] Running 1/0
 ✔ Container demo-app-server-1  Created                                                                              0.0s
Attaching to server-1
server-1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
server-1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
server-1  | [Wed Nov 29 11:30:02.099427 2023] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.57 (Debian) PHP/8.3.0 configured -- resuming normal operations
server-1  | [Wed Nov 29 11:30:02.099452 2023] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
```

## Step 17. Viewing the container using Docker Desktop


![Image1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4t77e1uvsgxru7i3g25p.png)




## Step 18. Accessing the PHP application

```
curl http://localhost:9000
Hello, Composer!
```