Understanding build secrets vs runtime secrets

3 min read Updated 4 days ago

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:

  1. User-defined secrets: The same secrets you add manually that were available during build

  2. 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:

  1. Security: Keeping service credentials out of the build process prevents them from being accidentally included in Docker image layers

  2. Flexibility: Services can be added, removed, or have their credentials rotated without rebuilding your application

  3. 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