Understanding build secrets vs runtime secrets
Understanding build secrets vs runtime secrets
When deploying applications, it's important to understand the difference between secrets available during the build process and those available when your application runs. This guide explains how each type works and why they're handled differently.
What are build secrets?
Build secrets are environment variables available only during the Docker image building process. These are the secrets you manually add in the "Environment secrets" section of your application settings.
Common uses for build secrets:
Private package repository tokens (NPM, Composer, etc.)
API keys needed to download dependencies
Authentication for private Git submodules
Build-time configuration values
What are runtime secrets?
Runtime secrets are environment variables available when your application container is running. These include both your manually added secrets and auto-injected service credentials.
Types of runtime secrets:
User-defined secrets: The same secrets you add manually that were available during build
Auto-injected service credentials: Automatically generated credentials from your application services (databases, cache, etc.)
Auto-injected service credentials
When you add services like MySQL, PostgreSQL, or Redis to your application, the platform automatically generates secure credentials and injects them as environment variables. You can view these in the "Injected secrets" modal.
Examples of auto-injected variables:
Database services:
DB_HOST
,DB_PORT
,DB_DATABASE
,DB_USERNAME
,DB_PASSWORD
Redis/Valkey:
REDIS_HOST
,REDIS_PORT
,REDIS_PASSWORD
MongoDB:
MONGO_HOST
,MONGO_PORT
,MONGO_DATABASE
,MONGO_USERNAME
,MONGO_PASSWORD
Why service credentials aren't available during build
Auto-injected service credentials are not available during the Docker build process. This is by design for several important reasons:
Security: Keeping service credentials out of the build process prevents them from being accidentally included in Docker image layers
Flexibility: Services can be added, removed, or have their credentials rotated without rebuilding your application
Separation of concerns: Build-time operations shouldn't depend on runtime services
Working with this limitation
If your application needs to run database migrations or perform other operations that require service access, you have several options:
1. Use init containers (recommended)
Configure your application to run migrations or setup tasks when the container starts. Make sure these commands can be ran multiple times because every instance of your application will always execute these commands.
2. Use conditional logic
Make your build scripts check for the presence of credentials:
if [ -n "$DB_HOST" ]; then
php artisan migrate
else
echo "Skipping migrations during build"
fi
Best practices
Keep build secrets minimal: Only add secrets that are absolutely necessary for the build process
Use init containers for setup: Run database migrations and other setup tasks when the container starts, not during build
Never hardcode credentials: Always use environment variables for both build and runtime secrets
Review injected secrets: Check the "Injected secrets" modal to see what environment variables are automatically available to your application