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.
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]);
First published: 20th May 2021