Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Docker - Create .env File On Startup

The Problem

When writing software today, one typically needs an .env file somewhere with all of your environment settings in it which are typically not stored in the code repository for security reasons. When using CI/CD with something like GitLab or Jenkins, one will typically pass these secrets/settings at the point of running the Docker container.

E.g.

docker-run \
  -e "DB_HOST=192.168.1.2" \
  -e "DB_USER=bob" \
  my-container-image-name

You will be able to view these variables if you were to enter the container as the root user and run the env command. However, your web application, running as the www-data user, will not necessarily have access to them, nor would you want it to have access to all of the settings. To work around this, I have my containers root user create the .env file for my web application, on startup of the container. The script below will do this.

This solution also resolves the issue with the cron service running with its own set of environment variables in the Docker container. Simply have the scripts that y our cron job calls, load the environment files using the dotenv package for your language of choice.

Script

<?php

/*
 * This script creates a .env file from expected variables specified in this file.
 * If we find that an expected environment variable is missing, then we notify you and exit with a -1 exit code.
 * Call it with php create-env-file.php [output filepath]
 */

define('REQUIRED_ENV_VARS', [
    "APP_KEY",
    "DB_HOST",
    "DB_USER",
    "DB_PASSWORD",
]);

define('OPTIONAL_ENV_VARS', [
    "DB_PORT",
]);

define('ALL_VARS', [...REQUIRED_ENV_VARS, ...OPTIONAL_ENV_VARS]);


function main(string $outputFilepath)
{
    $env = shell_exec("env");
    $filteredLines = [];
    $lines = explode(PHP_EOL, $env);

    foreach ($lines as $index => $line)
    {
        $parts = explode("=", $line);

        if (in_array($parts[0], ALL_VARS))
        {
            // Handle situations where the user has spaces in variable name, through the use of quotes.
            if (strpos($parts[1], " ") !== false)
            {
                $filteredLines[$parts[0]] = "$parts[0]=\"$parts[1]\"";
            }
            else
            {
                $filteredLines[$parts[0]] = $line;
            }
        }
    }


    $missingKeys = array_diff(REQUIRED_ENV_VARS, array_keys($filteredLines));

    if (count($missingKeys) > 0)
    {
        fwrite(STDERR, "Missing required environment variables: " . PHP_EOL);

        foreach ($missingKeys as $missingKey)
        {
            fwrite(STDERR, " - {$missingKey}" . PHP_EOL);
        }

        fwrite(STDERR, "... out of the following env vars: " . PHP_EOL);
        fwrite(STDERR, "{$env}" . PHP_EOL);

        exit(-1);
    }

    $content = implode(PHP_EOL, $filteredLines);
    file_put_contents($outputFilepath, $content);
}

if (count($argv) < 2)
{
    fwrite(STDERR, "Missing expected output filepath as parameter." . PHP_EOL);
    exit(-1);
}

main($argv[1]);
Last updated: 26th August 2022
First published: 20th May 2021