Build time and runtime environment variables
Build time and runtime environment variables
When deploying an application it helps to know which variables exist while your image is being built and which exist once your application is running. On Ploi Cloud the answer is simpler than you might expect: there is one list of environment secrets, and everything in it is available in both phases.
How environment secrets work
Everything you add under "Environment secrets" in your application settings is used twice:
- During the build: each secret is passed to the image build as a build argument, so your build commands can read it.
- At runtime: each secret is injected as an environment variable when your container starts.
You do not choose between the two, and there is no build-only or runtime-only setting. A secret named COMPOSER_AUTH is readable by Composer during the build and is also present in the environment of the running application.
Values are encrypted at rest, and changes only take effect after a new deployment.
Credentials injected by services
When you add a service such as MySQL, PostgreSQL, MongoDB, Redis or MinIO, the platform generates credentials for it and injects them into your application automatically. You can review them through the "Injected secrets" button in your application settings.
Examples of injected variables
- Database services:
DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD - Redis and Valkey:
REDIS_HOST,REDIS_PORT,REDIS_PASSWORD - MongoDB:
MONGO_HOST,MONGO_PORT,MONGO_DATABASE,MONGO_USERNAME,MONGO_PASSWORD
These are the one genuine exception to the rule above: injected credentials are available at runtime only, never during the build. That is deliberate:
- Security: service credentials stay out of the build entirely.
- Flexibility: services can be added, removed or rotated without rebuilding your application.
- Separation of concerns: a build should not depend on a running database.
Running tasks that need a database
Because injected credentials do not exist during the build, anything that talks to a service has to run later.
Use init commands (recommended)
Init commands run every time your application starts, after the injected credentials are available. Make sure they are safe to run repeatedly, because they execute on every deployment and restart.
Example init commands:
php artisan migrate --forcephp artisan config:cache
Or guard the command in your build
If you would rather keep a command in the build, make it check for the credentials first:
if [ -n "$DB_HOST" ]; then
php artisan migrate
else
echo "Skipping migrations during build"
fi
Build warnings about sensitive variable names
You may see a warning like this in your build logs:
Do not use ARG or ENV instructions for sensitive data (ARG "APP_KEY")
This comes from Docker BuildKit's built-in Dockerfile checks, not from Ploi Cloud. Since every environment secret is passed to the build as a build argument, BuildKit notices names that look sensitive, such as anything containing KEY, TOKEN, PASSWORD or SECRET, and points it out.
It is only a warning. Your build still succeeds, the variable is set correctly, and there is nothing to change. Every Laravel application produces it for APP_KEY, which the platform generates for you when the application is created.
Referencing other variables
You can build a value out of other environment secrets instead of repeating yourself.
How it works
- Use
${VARIABLE_NAME}to reference another secret - Use
${VARIABLE_NAME:-default}to fall back to a default when it is not set - References are resolved at runtime, so build arguments receive the raw value with the
${...}still in it - A secret can only reference your other environment secrets, not the credentials injected by a service
Examples
API_ENDPOINT: ${API_BASE_URL}/v${API_VERSION:-1}/api
APP_URL: https://${APP_DOMAIN}
MAIL_FROM_ADDRESS: noreply@${APP_DOMAIN}
Best practices
- Keep the list small: every secret is passed into the build as well, so only add what your application actually needs.
- Scope build credentials tightly: for tokens the build needs, such as registry or repository credentials, use read-only permissions and rotate them on their own schedule.
- Use init commands for setup: run migrations and other tasks that need a service when the container starts, not during the build.
- Never hardcode credentials: keep them in environment secrets rather than in your repository or your build commands.
- Review injected secrets: check the "Injected secrets" screen to see exactly which variables your services provide.
- Redeploy after changes: updating a secret has no effect until the next deployment.
Frequently asked questions
What's the difference between build secrets and runtime secrets?
There is no difference on Ploi Cloud. There is a single "Environment secrets" list, and every secret in it is passed to the image build as a build argument and injected as an environment variable when your container starts. The only values that behave differently are the credentials injected automatically by services, which exist at runtime only.
Can I make a secret available during the build but not at runtime?
No. Secrets cannot be scoped to a single phase. If a token is only needed by the build, give it the narrowest possible permissions and rotate it independently of the rest of your configuration.
Why do I see a warning about APP_KEY in my build logs?
That warning comes from Docker BuildKit, not from Ploi Cloud. Because every environment secret is passed to the build as a build argument, BuildKit flags variable names that look sensitive, such as anything containing KEY, TOKEN, PASSWORD or SECRET. The build is not affected and no action is needed.
Why are my database credentials empty during the build?
Credentials for services such as MySQL, PostgreSQL and Redis are injected at runtime only, so they are not set while the image is being built. Move anything that needs a database, such as migrations, into your init commands.
Can one environment secret reference another?
Yes. Use `${VARIABLE_NAME}` or `${VARIABLE_NAME:-default}` in the value. References are resolved at runtime and can only point at your own environment secrets, not at credentials injected by a service. Build arguments always receive the raw, unexpanded value.