During the starting of every devcontainer, a set of life cycle events are triggered. These events can be used to properly initialise the devcontainer like installation of some dependencies, starting of services etc. There are various events which are triggered, but few of them which are of relevant are listed below:

  • initializeCommand: A list of commands which are executed on the host machine during the initialisation phase of devcontainer.
  • postCreateCommand: Command which is executed on the devcontainer just after creation of the devcontainer on the container runtime. This command is only executed once.
  • postStartCommand: Command which is executed on the devcontainer after postCreateCommand and gets executed whenever the container is started.
  • postAttachCommand: Command which is executed on the devcontainer everytime a terminal/tool is attached to the container.

More life cycle events can be read from devcontainer official docs on lifecycle scripts.

PHP devcontainer

Building a PHP devcontainer is different then building a native Go devcontainer, as PHP application is a web server, which needs a service to be started, web server files to be located at a default location (default: /var/www/html) and exposes a port (default: 80).

To facilitate the appropriate running of our devcontainer, we will be using the life cycle events explained earlier in this section.

  1. Issue 1: Mount workspace files to /var/www/html: VS Code mounts the workspace folder at /workspaces/projectname and pwd is set to this location. A symbolic link of our pwd to /var/www/html solves our purpose, indirectly mounting our workspace to web-server location.
  2. Issue 2: Starting apache web-server: To start apache server, a CLI tool is provided by apache foundation named apache2ctl consisting of start sub-command (for running in background) or use apache2-foreground for running apache in foreground.
  3. Issue 3: Exposing apache port: Exposing the default port 80 to host machine’s non-root port is required like 8080. (as opening default ports may conflict with existing web-server installation’s on the host machine)

All the issues are resolved in the following devcontainer.json file.

{
    "name": "Test Application Dev Container",
    "image": "php:8.2.20-apache",
    "postCreateCommand": "rm -rf /var/www/html/; ln -s $(pwd) /var/www/html",
    "postStartCommand": "apache2ctl start",
    "appPort": ["8080:80"],
    "customizations": {
        "vscode": {
            "extensions": [
                "bmewburn.vscode-intelephense-client",
                "esbenp.prettier-vscode",
                "xdebug.php-debug"
            ],
            "settings": {
                "php.validate.executablePath": "/usr/local/bin/php",
                "php.validate.run": "onType"
            }
        }
    }
}

You can notice that, postCreateCommand is used linking our workspace directory to /var/www/html (as it is required only once), postStartCommand to start the apache server (as it is required everytime we run the devcontainer).

appPort is a key specification to map container ports to host. In the above example, we are mapping port 80 (from within container) to 8080 (host machine).

Now, let’s create a dummy index.php page.

<?php
header('Content-Type: text/html; charset=utf-8');
 
$title = "Welcome, Asura!";
$greeting = "Hello, Asura! Welcome to the mystical realm where power and wisdom intertwine.";
$funFact = "Did you know? Asura in mythology are powerful beings who possess immense strength and abilities. You're in good company!";
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo $title; ?></title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #ffcccc;
            color: #660000;
            text-align: center;
            padding: 50px;
            margin: 0;
        }
        h1 {
            color: #cc0000;
            font-size: 2.5em;
        }
        .fun-fact {
            background-color: #ff9999;
            color: #660000;
            padding: 20px;
            border-radius: 8px;
            display: inline-block;
            margin: 20px 0;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }
    </style>
</head>
<body>
    <h1><?php echo $greeting; ?></h1>
    <div class="fun-fact">
        <p><?php echo $funFact; ?></p>
    </div>
</body>
</html>

Let’s try to navigate to http://127.0.0.1:8080/ in our host machine.

Note

If while creating the devcontainer, host machine’s port 8080 is not free, then VS Code will generate a random port for assignment. You can check the assigned ports in the Ports Tab of the editor.

Alternatively, for the above PHP devcontainer, you can replace the postCreateCommand with the following settings, to mount our VS Code editor inside /var/www/html location and make our workspace location also as /var/www/html.

{
    //...
    "workspaceFolder": "/var/www/html",
    "mounts": [
        "source=${localWorkspaceFolder},target=/var/www/html,type=bind"
    ],
    //...
}

Great, we have learned about different life cycle events. In the next part of the series we will learn about creating a devcontainer using a Dockerfile.