Help us improve
Share bugs, ideas, or general feedback.
From all-skills
Guides Elixir application configuration, covering runtime vs compile-time settings, config.exs, runtime.exs, and Application.get_env.
npx claudepluginhub vinnie357/claude-skills --plugin qaHow this skill is triggered — by the user, by Claude, or both
Slash command
/all-skills:configThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Guide for proper application configuration in Elixir, with emphasis on understanding and correctly using runtime vs compile-time configuration.
Guides Elixir app configuration distinguishing runtime vs compile-time settings using config.exs/runtime.exs, Application.get_env, and env-specific files for apps, releases, libraries.
Prevents Phoenix deployment failures with 7 rules on runtime.exs env vars, release migrations, PHX_HOST, assets.deploy, secrets, health endpoint, logger config. Invoke before config/rel/Dockerfile changes.
Provides Elixir/Phoenix deployment patterns including runtime.exs for env vars/secrets, Dockerfile, fly.toml, mix releases, rel/ overlays, health checks. Use for Fly.io, Docker, CI/CD, migrations.
Share bugs, ideas, or general feedback.
Guide for proper application configuration in Elixir, with emphasis on understanding and correctly using runtime vs compile-time configuration.
Use this skill when:
config.exs and runtime.exsApplication.compile_env and Application.get_envuse Mix.Config to import ConfigRuntime configuration is the preferred approach. Only use compile-time configuration when values must affect compilation itself.
Evaluated during project compilation, before your application starts.
import Config
# Basic configuration
config :my_app, MyApp.Repo,
database: "my_app_dev",
username: "postgres",
password: "postgres",
hostname: "localhost"
# Environment-specific config
config :my_app,
environment: config_env()
# Import environment-specific config files
import_config "#{config_env()}.exs"
Key characteristics:
import Config (not use Mix.Config)config_env() and config_target()import_config/1Evaluated right before applications start in both Mix and releases.
import Config
# Read from environment variables
config :my_app, MyApp.Repo,
database: System.get_env("DATABASE_NAME") || "my_app_dev",
username: System.get_env("DATABASE_USER") || "postgres",
password: System.get_env("DATABASE_PASSWORD") || "postgres",
hostname: System.get_env("DATABASE_HOST") || "localhost",
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
# Conditional runtime configuration
if config_env() == :prod do
config :my_app, MyAppWeb.Endpoint,
secret_key_base: System.fetch_env!("SECRET_KEY_BASE"),
http: [port: String.to_integer(System.fetch_env!("PORT"))]
end
Key characteristics:
import_config/1System.get_env and System.fetch_env!Environment-specific compile-time configuration, typically imported from config.exs:
# config/config.exs
import_config "#{config_env()}.exs"
# config/dev.exs
import Config
config :my_app, MyApp.Repo,
show_sensitive_data_on_connection_error: true,
pool_size: 10
# config/test.exs
import Config
config :my_app, MyApp.Repo,
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 10
# config/prod.exs
import Config
# Production-specific compile-time config only
config :my_app, MyAppWeb.Endpoint,
cache_static_manifest: "priv/static/cache_manifest.json"
Use in function bodies to read configuration at runtime:
defmodule MyApp.Service do
def start_link do
# Get with default value
timeout = Application.get_env(:my_app, :timeout, 5000)
GenServer.start_link(__MODULE__, timeout, name: __MODULE__)
end
end
When to use:
defmodule MyApp.Mailer do
def deliver(email) do
# Raise if not configured (for required config)
api_key = Application.fetch_env!(:my_app, :mailgun_api_key)
send_email(email, api_key)
end
end
When to use:
defmodule MyApp.Cache do
def get(key) do
case Application.fetch_env(:my_app, :cache_adapter) do
{:ok, adapter} -> adapter.get(key)
:error -> nil # No caching configured
end
end
end
When to use:
Use only when configuration must affect compilation:
defmodule MyApp.JSONEncoder do
# Only use compile_env when the value affects compilation
@json_library Application.compile_env(:my_app, :json_library, Jason)
def encode(data) do
# The specific library is compiled into the module
@json_library.encode(data)
end
end
When to use:
Warning: Mix tracks compile-time config and raises errors if values diverge between compile and runtime.
defmodule MyApp.Adapter do
# Raises at compile time if not configured
@adapter Application.compile_env!(:my_app, :storage_adapter)
def store(data) do
@adapter.put(data)
end
end
When to use:
Correct approach:
# config/runtime.exs
import Config
config :my_app,
api_url: System.get_env("API_URL") || "http://localhost:4000",
api_key: System.fetch_env!("API_KEY") # Required in production
Access in code:
defmodule MyApp.Client do
def call(endpoint) do
api_url = Application.fetch_env!(:my_app, :api_url)
api_key = Application.fetch_env!(:my_app, :api_key)
HTTPoison.get("#{api_url}/#{endpoint}", [{"Authorization", api_key}])
end
end
config/config.exs:
import Config
# Shared configuration for all environments
config :my_app, :shared_setting, "value"
# Import environment-specific config
import_config "#{config_env()}.exs"
config/dev.exs:
import Config
config :my_app, MyApp.Repo,
database: "my_app_dev",
show_sensitive_data_on_connection_error: true
config/runtime.exs:
import Config
# Runtime config for all environments
if config_env() == :prod do
# Production-specific runtime config
database_url = System.fetch_env!("DATABASE_URL")
config :my_app, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end
Problem: Can't call config_env() at runtime.
Solution: Store it in config:
# config/config.exs
import Config
config :my_app, :environment, config_env()
# Then in your code:
defmodule MyApp do
def environment do
Application.fetch_env!(:my_app, :environment)
end
def development? do
environment() == :dev
end
end
defmodule MyApp.Telemetry do
def setup do
case Application.fetch_env(:my_app, :telemetry_backend) do
{:ok, :datadog} -> setup_datadog()
{:ok, :prometheus} -> setup_prometheus()
:error -> :ok # Telemetry disabled
end
end
end
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
MyApp.Repo,
{MyApp.Worker, Application.fetch_env!(:my_app, :worker_opts)},
MyAppWeb.Endpoint
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
# DON'T: Using compile_env for environment variables
defmodule MyApp.Service do
@api_key Application.compile_env(:my_app, :api_key)
def call do
# This won't work correctly in releases!
HTTPoison.get(url, [{"Authorization", @api_key}])
end
end
Why it's wrong: Environment variables aren't available at compile time in releases.
Correct approach:
defmodule MyApp.Service do
def call do
# Read at runtime
api_key = Application.fetch_env!(:my_app, :api_key)
HTTPoison.get(url, [{"Authorization", api_key}])
end
end
# DON'T: Directly access other app's configuration
defmodule MyApp do
def logger_level do
Application.get_env(:logger, :level) # Fragile coupling
end
end
Why it's wrong: Creates tight coupling and breaks encapsulation.
Correct approach:
# Configure it in your own app
# config/config.exs
config :my_app, :log_level, :info
# Then read your own config
defmodule MyApp do
def log_level do
Application.get_env(:my_app, :log_level, :info)
end
end
# DON'T: In a library
defmodule MyLibrary do
def process(data) do
# Library reading its own application environment
timeout = Application.get_env(:my_library, :timeout, 5000)
do_work(data, timeout)
end
end
Why it's wrong: Library config.exs is not evaluated when used as a dependency.
Correct approach:
# DO: Accept options as arguments
defmodule MyLibrary do
def process(data, opts \\ []) do
timeout = Keyword.get(opts, :timeout, 5000)
do_work(data, timeout)
end
end
# Users configure in their application
defmodule MyApp.Worker do
def run do
opts = Application.get_env(:my_app, :my_library_opts, [])
MyLibrary.process(data, opts)
end
end
# DON'T: Use Mix.env() in application code
defmodule MyApp do
def environment do
Mix.env() # Won't work in releases!
end
end
Why it's wrong: Mix is not available in production releases.
Correct approach:
# Store it in config
# config/config.exs
config :my_app, :environment, config_env()
# Access from application environment
defmodule MyApp do
def environment do
Application.fetch_env!(:my_app, :environment)
end
end
| Function | Description | Where to Use |
|---|---|---|
config/2 | Configure app with keyword list | All config files |
config/3 | Configure app key with value | All config files |
config_env/0 | Get current environment (:dev, :test, :prod) | All config files |
config_target/0 | Get build target | All config files |
import_config/1 | Import other config files | Not in runtime.exs |
| Function | Return Type | Use Case |
|---|---|---|
Application.get_env/3 | value | default | Runtime with default |
Application.fetch_env/2 | {:ok, value} | :error | Runtime with pattern matching |
Application.fetch_env!/2 | value (raises if missing) | Required runtime config |
Application.compile_env/3 | value | Compile-time with default |
Application.compile_env!/2 | value (raises if missing) | Required compile-time config |
use Mix.Config to import ConfigOld (deprecated):
use Mix.Config
config :my_app, :key, "value"
if Mix.env() == :prod do
config :my_app, :production, true
end
import_config "#{Mix.env()}.exs"
New:
import Config
config :my_app, :key, "value"
if config_env() == :prod do
config :my_app, :production, true
end
import_config "#{config_env()}.exs"
Changes:
use Mix.Config with import ConfigMix.env() with config_env()Before (all in config.exs):
# config/config.exs
import Config
config :my_app,
api_key: System.get_env("API_KEY"), # Wrong place!
static_value: "something"
After (split correctly):
# config/config.exs
import Config
config :my_app,
static_value: "something"
# config/runtime.exs
import Config
config :my_app,
api_key: System.get_env("API_KEY") || raise("API_KEY not set")
Application.get_env/3 in function bodiesconfig.exsconfig_env() in config files, store resultfetch_env!/2 in application start for required valuesget_env/3 with defaults for optional configconfig_env() outside config files# In IEx
Application.get_all_env(:my_app)
# Check specific key
Application.fetch_env(:my_app, :some_key)
# See all applications
Application.loaded_applications()
Problem: Config not available in tests
# config/test.exs
import Config
config :my_app, :test_value, "configured"
Problem: Different values in dev vs release
Check that runtime.exs is being used and environment variables are set correctly.
Problem: Compile-time config not updating
# Clean and recompile
mix clean
mix compile
"Reading the application environment at runtime is the preferred approach."
"If you are writing a library to be used by other developers, it is generally recommended to avoid the application environment, as the application environment is effectively a global storage."
"config/config of a library is not evaluated when the library is used as a dependency, as configuration is always meant to configure the current project."
Configuration is a cross-cutting concern. Default to runtime configuration with Application.get_env/3, and only reach for compile-time configuration when you have a specific need for it that justifies the trade-offs.