### Install dependencies Source: https://dotenvy.hexdocs.pm/getting_started.html Navigate to the project directory and fetch dependencies. ```bash cd example mix deps.get ``` -------------------------------- ### Define database variables in envs/dev.env Source: https://dotenvy.hexdocs.pm/cheatsheet.html Example configuration for development database settings. ```text PG_USERNAME=postgres PG_PASSWORD=postgres PG_HOSTNAME=localhost PG_PORT=5432 PG_DATABASE=m_app_dev PG_POOL_SIZE=10 PG_POOL=DBConnection.ConnectionPool PG_SSL=true ``` -------------------------------- ### Install local application in Livebook Source: https://dotenvy.hexdocs.pm/livebooks.html Configure Mix.install to include a local application that utilizes Dotenvy. ```elixir Mix.install( [ {:app_using_dotenvy, path: "/path/to/app_using_dotenvy", env: :dev} ], config_path: :app_using_dotenvy, lockfile: :app_using_dotenvy ) ``` -------------------------------- ### Define database variables in envs/test.env Source: https://dotenvy.hexdocs.pm/cheatsheet.html Example configuration for test database settings. ```text PG_USERNAME=postgres PG_PASSWORD=postgres PG_HOSTNAME=localhost PG_PORT=5432 PG_DATABASE=my_app_test PG_POOL_SIZE=10 PG_POOL=Ecto.Adapters.SQL.Sandbox PG_SSL=false ``` -------------------------------- ### Define environment variables in .env file Source: https://dotenvy.hexdocs.pm/ Example content for a .env file used to store configuration defaults. ```text # .env DATABASE=myapp_dev USERNAME=myuser PASSWORD=mypassword HOSTNAME=localhost POOL_SIZE=10 POOL= ``` -------------------------------- ### Reference system variables in .env Source: https://dotenvy.hexdocs.pm/Dotenvy.html Example of a .env file using variable substitution with system environment variables. ```text # .env CACHE_DIR=${HOME}/cache ``` -------------------------------- ### Example variable names Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Examples of valid and invalid variable naming conventions. ```text DATABASE_URL # ok foobar # ok NO-WORK # <-- invalid !!! ÜBER # <-- invalid !!! 2MUCH # <-- invalid !!! ``` -------------------------------- ### Examples of to!/2 usage Source: https://dotenvy.hexdocs.pm/Dotenvy.Transformer.html Demonstrates various type conversions including booleans, integers, modules, and custom transformation functions. ```elixir iex> to!("debug", :atom) :debug iex> to!("", :boolean) false iex> to!("", :boolean?) nil iex> to!("5432", :integer) 5432 iex> to!("DateTime", :module) DateTime iex> to!("foo", fn val -> val <> "bar" end) "foobar" iex> Dotenvy.Transformer.to!("Oops", :float) ** (Dotenvy.Error) Unparsable as float (dotenvy 1.0.0) lib/dotenvy/transformer.ex:165: Dotenvy.Transformer.to!/2 ``` -------------------------------- ### Configure Swoosh Mailer Adapter Source: https://dotenvy.hexdocs.pm/phoenix.html Example configuration for setting a production mailer adapter and its required API client. ```elixir config :your_app, YourApp.Mailer, adapter: Swoosh.Adapters.Mailgun, api_key: System.get_env("MAILGUN_API_KEY"), domain: System.get_env("MAILGUN_DOMAIN") ``` ```elixir config :swoosh, :api_client, Swoosh.ApiClient.Hackney ``` -------------------------------- ### Integrating Legacy Secrets in runtime.exs Source: https://dotenvy.hexdocs.pm/1password.html Example configuration for loading external shell-sourced environment variables into Dotenvy. ```elixir # config/runtime.exs import Config import Dotenvy env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") {raw_envs, _} = System.shell(~s'bash -c "source #{Path.absname("secrets.sh", env_dir_prefix)} && env"') {:ok, system_env_vars} = Dotenvy.Parser.parse(raw_envs) source!([ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), system_env_vars ]) ``` -------------------------------- ### Install Dotenvy Generators Source: https://dotenvy.hexdocs.pm/generators.html Commands to remove existing Phoenix generators and install the Dotenvy generator scripts. ```bash mix archive.uninstall phx_new mix archive.install hex dotenvy_generators ``` -------------------------------- ### Example error output Source: https://dotenvy.hexdocs.pm/cheatsheet.html The format of the error message when a custom validation fails. ```text ** (RuntimeError) Error converting variable SOME_BOOL using custom function: strict_boolean! values must be either true or false ``` -------------------------------- ### Defining a configuration contract Source: https://dotenvy.hexdocs.pm/getting_started.html Uses env! to mandate that a specific environment variable must be present for the application to start. ```elixir config :example, :password, env!("PASSWORD", :string!) ``` -------------------------------- ### Providing environment variables via command line Source: https://dotenvy.hexdocs.pm/getting_started.html Passing a required variable directly to the application startup command. ```bash PASSWORD=xxxx iex -S mix ``` -------------------------------- ### Configure runtime settings in runtime.exs Source: https://dotenvy.hexdocs.pm/minimal.html Use runtime.exs to load environment variables dynamically at startup, ensuring sensitive or environment-specific data is injected correctly. ```elixir import Config import Dotenvy env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!([ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ]) config :myapp, MyApp.Repo, database: env!("DATABASE", :string!), username: env!("USERNAME", :string), password: env!("PASSWORD", :string), hostname: env!("HOSTNAME", :string!), pool_size: env!("POOL_SIZE", :integer), adapter: env("ADAPTER", :module, Ecto.Adapters.Postgres), pool: env!("POOL", :module?) # etc... ``` -------------------------------- ### Reference system variables before and after Source: https://dotenvy.hexdocs.pm/Dotenvy.html Sourcing system variables both before and after .env files to handle substitution and final precedence. ```elixir iex> Dotenvy.source([System.get_env(), ".env", System.get_env()]) ``` -------------------------------- ### Define basic key-value pairs Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Standard format for storing environment variables in a .env file. ```text S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE ``` -------------------------------- ### Use export prefix Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Optional prefix to allow sourcing the file directly in Bash. ```bash export S3_BUCKET=YOURS3BUCKET export SECRET_KEY=YOURSECRETKEYGOESHERE ``` -------------------------------- ### Configure Development Environment Variables Source: https://dotenvy.hexdocs.pm/phoenix.html Defines local development settings including database connection strings and server ports. ```text # envs/.dev.env HTTP_INTERFACE=127.0.0.1 HTTP_CHECK_ORIGIN=false PORT=4000 SECRET_KEY_BASE=6D1kPrXseBg9F1O9cZUd8ocIH7l3OgT9ZlopIXqDr+jYjNrcvbjqwNvzHPKIakcF PHX_URL_PORT=${PORT} PHX_PLUGIN_INIT_MODE=runtime PG_USERNAME=postgres PG_PASSWORD=postgres PG_HOSTNAME=localhost PG_DATABASE=your_app_dev PG_POOL=DBConnection.ConnectionPool POOL_SIZE=10 # DATABASE_URL format is `ecto://USER:PASS@HOST/DATABASE` DATABASE_URL=ecto://${PG_USERNAME}:${PG_PASSWORD}@${PG_HOSTNAME}/${PG_DATABASE} SHOW_SENSITIVE_DATA_ON_CONNECTION_ERROR=true ECTO_IPV6=false ECTO_STACKTRACE=true ``` -------------------------------- ### Configure database runtime settings Source: https://dotenvy.hexdocs.pm/cheatsheet.html Use env! to map environment variables to database configuration keys. ```elixir import Config import Dotenvy env_dir = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!([ Path.absname(".env", dir), Path.absname(".#{config_env()}.env", env_dir), Path.absname(".#{config_env()}.overrides.env", env_dir), System.get_env() ]) config :my_app, MyApp.PGRepo, pool: env!("PG_POOL", :module?), pool_size: env!("PG_POOL_SIZE", :integer), database: env!("PG_DATABASE", :string), username: env!("PG_USERNAME", :string), password: env!("PG_PASSWORD", :string), port: env!("PG_PORT", :integer), hostname: env!("PG_HOSTNAME", :string) ``` -------------------------------- ### Configure Production Environment Variables Source: https://dotenvy.hexdocs.pm/phoenix.html Defines production settings, including host-specific configurations and security-hardened defaults. ```text # envs/.prod.env ENABLE_DISTRIBUTED_MODE=true # DNS_CLUSTER_QUERY= # set by Fly.io HTTP_INTERFACE=0:0:0:0:0:0:0:0 HTTP_CHECK_ORIGIN=true # PORT= # set by Fly.io # SECRET_KEY_BASE= # Set by Fly.io PHX_SERVER=true PHX_HOST=your-app-website.com PHX_URL_PORT=443 PHX_URL_SCHEME=https PHX_CACHE_STATIC_MANIFEST=priv/static/cache_manifest.json PHX_STACKTRACE_DEPTH=false PHX_PLUGIN_INIT_MODE=compile # DATABASE_URL= # set by Fly.io SHOW_SENSITIVE_DATA_ON_CONNECTION_ERROR=false ECTO_IPV6=true ECTO_STACKTRACE=false SWOOSH_API_CLIENT=Swoosh.ApiClient.Finch SWOOSH_LOCAL_MEMORY_STORAGE=false # For production it's recommended to configure a different adapter... # SWOOSH_MAILER_ADAPTER=Swoosh.Adapters.Local ``` -------------------------------- ### source!(files, opts) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Loads environment variables from the specified files. This function returns a map of the loaded variables on success or raises an error if the operation fails. ```APIDOC ## source!(files, opts) ### Description Loads environment variables from one or more files. Returns a map of the environment variables on success or raises an exception on failure. ### Signature `source!(files :: binary() | [binary()], opts :: keyword()) :: %{optional(String.t()) => String.t()} | no_return()` ### Parameters - **files** (binary() | [binary()]) - Required - A single file path or a list of file paths to load. - **opts** (keyword()) - Required - Configuration options for the loading process. ``` -------------------------------- ### Generate a new Dotenvy application Source: https://dotenvy.hexdocs.pm/getting_started.html Use the mix task to scaffold a new project with Dotenvy support. ```bash mix dot.new example ``` -------------------------------- ### Configure release overlays in mix.exs Source: https://dotenvy.hexdocs.pm/cheatsheet.html Include the envs directory in the application release. ```elixir defp releases do [ my_app: [ overlays: ["envs/"] ] ] end ``` -------------------------------- ### Environment Variable Configuration File Source: https://dotenvy.hexdocs.pm/phoenix.html Default configuration values for Phoenix, database, and mailer settings stored in a .env file. ```bash # envs/.env (default/shared config) # ################### # Distributed Node # # ################### # If distributed mode is enabled, the DNS_CLUSTER_QUERY variable must have a value ENABLE_DISTRIBUTED_MODE=false DNS_CLUSTER_QUERY= # ##################### # Phoenix / Webserver # # ##################### # The interface(s) to listen on. # Can be specified as the following formats: # `1.2.3.4` for IPv4 addresses (using period separators) # `1:2:3:4:5:6:7:8` for IPv6 addresses (using colon separators) # # To bind to loopback IPv4 address & prevent access from other machines: `127.0.0.1` # To allow access from other machines: `0.0.0.0` # # To enable IPv6 and bind on all interfaces: `0:0:0:0:0:0:0:0` # For local network only access: `0:0:0:0:0:0:0:1` # # See the documentation on https://hexdocs.pm/bandit/Bandit.html#t:options/0 # for details about using IPv6 vs IPv4 and loopback vs public addresses. HTTP_INTERFACE=127.0.0.1 HTTP_CHECK_ORIGIN=false # Port where HTTP requests will be accepted PORT=4000 # A secret key used as a base to generate secrets for encrypting and signing data. # For example, cookies and tokens are signed by default, but they may also be # encrypted if desired. Must be set per application. SECRET_KEY_BASE= # Boolean indicating whether to start the Phoenix webserver implicitly. # Usually this is false for dev and true for prod PHX_SERVER=false # PHX_HOST, PHX_URL_PORT, and PHX_URL_SCHEME are used to create links PHX_HOST=localhost # Duplicate the ${PORT} value for http; 443 recommended for https PHX_URL_PORT= # scheme may be either http or https PHX_URL_SCHEME=http # Specifies the path to a cache manifes containing the digested version of # static files. This manifest is generated by the `mix assets.deploy` task, # which you should run after static files are built and # before starting your production server. # Leave empty for dev or when you do not need a manifest PHX_CACHE_STATIC_MANIFEST= # Set a higher stacktrace during development, e.g. 20, or set to false to disable. # Disabling is recommended in prod as building large stacktraces may be expensive. PHX_STACKTRACE_DEPTH=20 # Initialize plugs at runtime for faster development compilation # values can be :runtime or :compile; must be :compile in prod (the default) PHX_PLUGIN_INIT_MODE=compile ############ # Database # ############ # The MIX_TEST_PARTITION environment variable can be used # to provide built-in test partitioning in CI environment. # Run `mix help test` for more information. MIX_TEST_PARTITION= PG_USERNAME=postgres PG_PASSWORD=postgres PG_HOSTNAME=localhost PG_DATABASE=your_app_dev PG_POOL=DBConnection.ConnectionPool POOL_SIZE=10 # DATABASE_URL format is `ecto://USER:PASS@HOST/DATABASE` DATABASE_URL=ecto://${PG_USERNAME}:${PG_PASSWORD}@${PG_HOSTNAME}/${PG_DATABASE} SHOW_SENSITIVE_DATA_ON_CONNECTION_ERROR=true ECTO_IPV6=false ECTO_STACKTRACE=true ################### # Swoosh / Mailer # ################### # Specify a module as a Swoosh API client for production adapters or set to false # to disable. See https://hexdocs.pm/swoosh/Swoosh.ApiClient.html SWOOSH_API_CLIENT=false SWOOSH_LOCAL_MEMORY_STORAGE=true # By default it uses the Swoosh.Adapters.Local adapter which stores the emails # locally. You can see the emails in your browser, at "/dev/mailbox". # For production it's recommended to configure a different adapter SWOOSH_MAILER_ADAPTER=Swoosh.Adapters.Local ``` -------------------------------- ### Source multiple files with system precedence Source: https://dotenvy.hexdocs.pm/Dotenvy.html A common pattern for sourcing environment-specific files and overriding them with system environment variables. ```elixir iex> Dotenvy.source([ "#{config_env()}.env", "#{config_env()}.override.env", System.get_env() ]) ``` -------------------------------- ### Customize Environment Directory Source: https://dotenvy.hexdocs.pm/releases.html Update both runtime.exs and mix.exs when using a custom directory name for .env files instead of the default. ```elixir # config/runtime.exs env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./xyz") # ... etc... ``` ```elixir # mix.exs defp releases do [ my_app: [ overlays: ["xyz/"] ] ] end ``` -------------------------------- ### Configure Runtime Environment Loading Source: https://dotenvy.hexdocs.pm/releases.html Load environment variables from .env files in the specified directory within config/runtime.exs, ensuring absolute paths are used. ```elixir import Config import Dotenvy # For local development, read dotenv files inside the envs/ dir; # for releases, read them at the RELEASE_ROOT env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!([ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ]) ``` ```elixir env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!([ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ]) ``` -------------------------------- ### Include system variables for substitution Source: https://dotenvy.hexdocs.pm/Dotenvy.html Passing system environment variables as the first input to enable substitution within .env files. ```elixir iex> Dotenvy.source([System.get_env(), ".env"]) ``` -------------------------------- ### Configure compile-time settings in config.exs Source: https://dotenvy.hexdocs.pm/minimal.html Use environment-specific blocks within config.exs to handle compile-time configuration without separate environment files. ```elixir # compile-time config import Config # Dev if config_env() == :dev do config :logger, :console, level: :debug, format: "[$level] $message\n" end # Test if config_env() == :test do config :logger, :console, level: :warning, format: "[$level] $message\n" end # Prod if config_env() == :prod do config :logger, :console, format: "$time $metadata[$level] $message\n", level: :info, metadata: [:request_id] end ``` -------------------------------- ### source!(files, opts \ []) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Similar to source/2, but returns the accumulated map on success or raises an error on failure. ```APIDOC ## source!(files, opts \ []) ### Description As source/2, but returns a map on success or raises on error. ### Parameters - **files** (input_source) - Required - A path to an env file or a map of key-value pairs. - **opts** (Keyword) - Optional - Configuration options for the source operation. ``` -------------------------------- ### Dynamic Swoosh and Mailer Configuration Source: https://dotenvy.hexdocs.pm/phoenix.html Configuration logic using environment variables to determine the API client and mailer adapter at runtime. ```elixir if env!("SWOOSH_API_CLIENT", :boolean) do config :swoosh, api_client: env!("SWOOSH_API_CLIENT", :module!), finch_name: YourApp.Finch else config :swoosh, api_client: false end config :your_app, YourApp.Mailer, adapter: env!("SWOOSH_MAILER_ADAPTER", :module) config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: YourApp.Finch config :swoosh, local: env!("SWOOSH_LOCAL_MEMORY_STORAGE", :boolean) ``` -------------------------------- ### Parse a single file Source: https://dotenvy.hexdocs.pm/Dotenvy.html The simplest implementation of source/2 to parse a single .env file. ```elixir iex> Dotenvy.source(".env") {:ok, %{ "TIMEOUT" => "5000", "DATABASE_URL" => "postgres://postgres:postgres@localhost/myapp", # ...etc... } } ``` -------------------------------- ### Configure Test Environment Variables Source: https://dotenvy.hexdocs.pm/phoenix.html Defines test-specific settings, utilizing the Ecto SQL Sandbox for database operations. ```text # envs/.test.env HTTP_INTERFACE=127.0.0.1 HTTP_CHECK_ORIGIN=false PORT=4002 SECRET_KEY_BASE=514niO7SLGRKi01EN1fQUqekoyfHSQL0640m64tAkJSPG7anLA6iPxWrcIUzCgyA PHX_URL_PORT=${PORT} PHX_PLUGIN_INIT_MODE=runtime PG_USERNAME=postgres PG_PASSWORD=postgres PG_HOSTNAME=localhost PG_DATABASE=your_app_test PG_POOL=Ecto.Adapters.SQL.Sandbox POOL_SIZE=10 # DATABASE_URL format is `ecto://USER:PASS@HOST/DATABASE` DATABASE_URL=ecto://${PG_USERNAME}:${PG_PASSWORD}@${PG_HOSTNAME}/${PG_DATABASE} SHOW_SENSITIVE_DATA_ON_CONNECTION_ERROR=true ECTO_IPV6=false ECTO_STACKTRACE=true SWOOSH_MAILER_ADAPTER=Swoosh.Adapters.Test ``` -------------------------------- ### Configure Dotenvy with Absolute Paths Source: https://dotenvy.hexdocs.pm/flyio.html Use Path.absname/2 to ensure environment files are correctly located regardless of the RELEASE_ROOT value. ```elixir env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!( [ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ], require_files: [Path.absname(".env", env_dir_prefix)] ) ``` -------------------------------- ### Configure config/config.exs for Phoenix Source: https://dotenvy.hexdocs.pm/phoenix.html Defines general application settings, endpoint configuration, and asset build tools. Ensure placeholders like YourApp are replaced with your actual application module names. ```elixir # config/config.exs # This file is responsible for configuring your application # and its dependencies with the aid of the Config module. # # This configuration file is loaded before any dependency and # is restricted to this project. # General application configuration import Config config :your_app, ecto_repos: [YourApp.Repo], generators: [timestamp_type: :utc_datetime] # Configures the endpoint config :your_app, YourAppWeb.Endpoint, adapter: Bandit.PhoenixAdapter, render_errors: [ formats: [html: YourAppWeb.ErrorHTML, json: YourAppWeb.ErrorJSON], layout: false ], pubsub_server: YourApp.PubSub, live_view: [signing_salt: "xyz123AB"] # Configure esbuild (the version is required) config :esbuild, version: "0.17.11", your_app: [ args: ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), cd: Path.expand("../assets", __DIR__), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] # Configure tailwind (the version is required) config :tailwind, version: "3.4.3", your_app: [ args: ~w( --config=tailwind.config.js --input=css/app.css --output=../priv/static/assets/app.css ), cd: Path.expand("../assets", __DIR__) ] # Configures Elixir's Logger (compile-time config) config :logger, :console, format: "$time $metadata[$level] $message\n", metadata: [:request_id] # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" ``` -------------------------------- ### Configure Phoenix Runtime with Dotenvy Source: https://dotenvy.hexdocs.pm/phoenix.html Uses Dotenvy to load environment variables at runtime for database, endpoint, and server settings. ```elixir # config/runtime.exs import Config import Dotenvy # For local development, read dotenv files inside the envs/ dir; # for releases, read them at the RELEASE_ROOT env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!( [ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ], require_files: [Path.absname(".env", env_dir_prefix)] ) # config/runtime.exs is executed for all environments, including # during releases. It is executed after compilation and before the # system starts. Do not define any compile-time configuration in here, # as it won't be applied. # ## Using releases # # If you use `mix release`, you need to explicitly enable the server # by passing the PHX_SERVER=true when you start it: # # PHX_SERVER=true bin/your_app start # # Alternatively, you can use `mix phx.gen.release` to generate a `bin/server` # script that automatically sets the env var above. if env!("PHX_SERVER", :boolean!) do config :your_app, YourAppWeb.Endpoint, server: true end # Initialize plugs at runtime for faster development compilation # values can be :runtime or :compile; must be :compile in prod (the default) config :phoenix, :plug_init_mode, env!("PHX_PLUGIN_INIT_MODE", :existing_atom!) # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason ip = env!("HTTP_INTERFACE", fn val -> with [_] <- String.split(val, "."), [_] <- String.split(val, ":") do raise "Invalid IP address specified" else parts -> parts |> Enum.map(&String.to_integer/1) |> List.to_tuple() end end) ecto_socket_options = if env!("ECTO_IPV6", :boolean!), do: [:inet6], else: [] config :your_app, YourApp.Repo, # ssl: true, url: env!("DATABASE_URL", :string!), pool: env!("PG_POOL", :module?), pool_size: env!("POOL_SIZE", :integer!), socket_options: ecto_socket_options, stacktrace: env!("ECTO_STACKTRACE", :boolean), show_sensitive_data_on_connection_error: env!("SHOW_SENSITIVE_DATA_ON_CONNECTION_ERROR", :boolean) if env!("ENABLE_DISTRIBUTED_MODE", :boolean) do config :your_app, :dns_cluster_query, env!("DNS_CLUSTER_QUERY", :string) end config :your_app, YourAppWeb.Endpoint, cache_static_manifest: env!("PHX_CACHE_STATIC_MANIFEST", :string?), check_origin: env!("HTTP_CHECK_ORIGIN", :boolean), http: [ip: ip, port: env!("PORT", :integer!)], secret_key_base: env!("SECRET_KEY_BASE", :string!), # Used to build URLs url: [ host: env!("PHX_HOST", :string!), port: env!("PHX_URL_PORT", :integer!), scheme: env!("PHX_URL_SCHEME", :string!) ] # ## SSL Support # # To get SSL working, you will need to add the `https` key # to your endpoint configuration: # # config :your_app, YourAppWeb.Endpoint, # https: [ # ..., # port: 443, # cipher_suite: :strong, # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), # certfile: System.get_env("SOME_APP_SSL_CERT_PATH") # ] # # The `cipher_suite` is set to `:strong` to support only the # latest and more secure SSL ciphers. This means old browsers # and clients may not be supported. You can set it to # `:compatible` for wider support. # # `:keyfile` and `:certfile` expect an absolute path to the key # and cert in disk or a relative path inside priv, for example # "priv/ssl/server.key". For all supported SSL configuration # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 # # We also recommend setting `force_ssl` in your config/prod.exs, # ensuring no data is ever sent via http, always redirecting to https: # # config :your_app, YourAppWeb.Endpoint, # force_ssl: [hsts: true] # # Check `Plug.SSL` for all available options in `force_ssl`. # # In order to use HTTPS in development, a self-signed # certificate can be generated by running the following # Mix task: # # mix phx.gen.cert # # Run `mix help phx.gen.cert` for more information. # # The `http:` config above can be replaced with: # # https: [ # port: 4001, # cipher_suite: :strong, # keyfile: "priv/cert/selfsigned_key.pem", # certfile: "priv/cert/selfsigned.pem" # ], # # If desired, both `http:` and `https:` keys can be # configured to run both http and https servers on # different ports. ``` -------------------------------- ### Configure config/dev.exs for Phoenix Source: https://dotenvy.hexdocs.pm/phoenix.html Sets up development-specific features like code reloading, debug errors, and asset watchers. ```elixir # config/dev.exs import Config # Compile-time configuration includes code_reloader, debug_errors, and force_ssl # https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-compile-time-configuration config :your_app, YourAppWeb.Endpoint, code_reloader: true, debug_errors: true, force_ssl: false, # Watch static and templates for browser reloading. live_reload: [ patterns: [ ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", ~r"lib/your_app_web/(controllers|live|components)/.*(ex|heex)$" ] ], # The watchers configuration can be used to run external # watchers to your application. For example, we can use it # to bundle .js and .css sources. # Watchers can be configured at runtime but are unlikely to change watchers: [ esbuild: {Esbuild, :install_and_run, [:your_app, ~w(--sourcemap=inline --watch)]}, tailwind: {Tailwind, :install_and_run, [:your_app, ~w(--watch)]} ] # Compile-time config config :phoenix_live_view, debug_heex_annotations: true, enable_expensive_runtime_checks: true # Enable dev routes for dashboard and mailbox (compile-time config) config :your_app, dev_routes: true # Do not include metadata nor timestamps in development logs config :logger, :console, format: "[$level] $message\n" ``` -------------------------------- ### Configure config/test.exs for Phoenix Source: https://dotenvy.hexdocs.pm/phoenix.html Sets logging levels for the test environment. ```elixir # config/test.exs import Config # Print only warnings and errors during test config :logger, level: :warning ``` -------------------------------- ### source(files, opts \ []) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Accumulates values from the given input sources and stores them for later retrieval via env! functions. ```APIDOC ## source(files, opts \ []) ### Description Accumulates values from the given input(s). The accumulated values are stored via a side effect function to make them available to the env!/2 and env!/3 functions. ### Parameters - **files** (input_source) - Required - A path to an env file or a map of key-value pairs. - **opts** (Keyword) - Optional - Configuration options for the source operation. ``` -------------------------------- ### Configure runtime environment variables Source: https://dotenvy.hexdocs.pm/getting_started.html Define environment variables in runtime.exs using the env! macro. ```elixir config :example, :secret, env!("SECRET", :string!) ``` -------------------------------- ### Dotenvy.source(inputs, opts) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Accumulates values from the given input(s) and stores them for use by env!/2 and env!/3 functions. Inputs are processed from left to right, with right-most inputs taking precedence. ```APIDOC ## Dotenvy.source(inputs, opts) ### Description Accumulates values from the given input(s) and stores them via a side effect function. This function acts as a merging operation for maps or paths to environment files. ### Signature `source(inputs :: input_source() | [input_source()], opts :: keyword()) :: {:ok, %{optional(String.t()) => String.t()}} | {:error, any()}` ### Parameters - **inputs** (input_source() | [input_source()]) - Required - A single source or a list of sources (maps or file paths) to be merged. - **opts** (keyword()) - Optional - Configuration options for the parsing process. ### Options - **:parser** (module) - Module implementing the Dotenvy.parse/3 callback. Default: Dotenvy.Parser. - **:require_files** (boolean | [String.t()]) - Specifies which files must be present. Default: false. - **:side_effect** (function) - An arity 1 function called after successful parsing to store the values. Default: internal function storing values in the process dictionary. ### Example ```elixir # Parsing a single file Dotenvy.source(".env") # Merging multiple sources with precedence Dotenvy.source([ "#{config_env()}.env", "#{config_env()}.override.env", System.get_env() ]) ``` ``` -------------------------------- ### Executing Shell Commands for Environment Variables Source: https://dotenvy.hexdocs.pm/1password.html Use System.shell to execute a script and capture the environment output. ```elixir System.shell(~s'bash -c "source secrets.sh && env"') ``` -------------------------------- ### Source environment variables in config/runtime.exs Source: https://dotenvy.hexdocs.pm/cheatsheet.html Load environment variables during runtime, prioritizing system environment variables. ```elixir import Config import Dotenvy env_dir = System.get_env("RELEASE_ROOT") || Path.expand("./envs/") source!([ Path.absname(".env", env_dir), Path.absname(".#{config_env()}.env", env_dir), Path.absname(".#{config_env()}.overrides.env", env_dir), System.get_env() ]) ``` -------------------------------- ### Update source precedence with variable interpolation Source: https://dotenvy.hexdocs.pm/ Include System.get_env() before .env files to make system variables available for interpolation within the files. ```elixir # in dotenvy 0.5.0 or before: source(["#{config_env()}.env", "#{config_env()}.override.env"]) # should be changed to the following in dotenvy 0.6.0: source([System.get_env(), "#{config_env()}.env", "#{config_env()}.override.env", System.get_env()]) ``` -------------------------------- ### Define values with different quoting styles Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Demonstrates simple assignment, interpolation, and multi-line values. ```text SIMPLE=xyz123 INTERPOLATED="Multiple\nLines and variable substitution: ${SIMPLE}" NON_INTERPOLATED='raw text without variable interpolation' MULTILINE = """ long text here, e.g. a private SSH key """ ``` -------------------------------- ### Define Phoenix compile-time configuration Source: https://dotenvy.hexdocs.pm/minimal.html Certain framework-level settings like routes and LiveView annotations must be defined at compile-time to influence application generation. ```elixir # Compile-time config config :phoenix_live_view, debug_heex_annotations: true, enable_expensive_runtime_checks: true # Enable dev routes for dashboard and mailbox (compile-time config) config :your_app, dev_routes: true ``` -------------------------------- ### Resolve absolute paths for .env files Source: https://dotenvy.hexdocs.pm/livebooks.html Use Path.expand and Path.absname to ensure .env files are located correctly regardless of the current working directory. ```elixir env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") source!([ Path.absname(".env", env_dir_prefix), Path.absname(".#{config_env()}.env", env_dir_prefix), Path.absname(".#{config_env()}.overrides.env", env_dir_prefix), System.get_env() ]) ``` -------------------------------- ### Define environment-specific variables Source: https://dotenvy.hexdocs.pm/getting_started.html Declare variables in the environment-specific .env file. ```text SECRET=my-secret-dev ``` -------------------------------- ### Ensure Mix task configuration loading Source: https://dotenvy.hexdocs.pm/ Methods to ensure application configuration is loaded when executing custom Mix tasks. ```elixir def run(_args) { Mix.Task.run("app.config") # ... } ``` ```bash mix do app.config other.task ``` ```elixir # mix.exs defp aliases do [ "other.task": ["app.config", "other.task"] ] ``` -------------------------------- ### Update source precedence for system environment variables Source: https://dotenvy.hexdocs.pm/ Include System.get_env() as the final argument to ensure system variables take precedence over .env files. ```elixir # in dotenvy 0.5.0 or before: source(["#{config_env()}.env", "#{config_env()}.override.env"]) # should be changed to the following in dotenvy 0.6.0: source(["#{config_env()}.env", "#{config_env()}.override.env", System.get_env()]) ``` -------------------------------- ### Update Dockerfile for Dotenvy Source: https://dotenvy.hexdocs.pm/flyio.html Add the COPY command to the Dockerfile to ensure the environment directory is included in the release build. ```dockerfile # ... existing Docker stuff... # Compile the release RUN mix compile # Changes to config/runtime.exs don't require recompiling the code COPY config/runtime.exs config/ COPY rel rel # <---- make the directory of envs available to the release!!! COPY envs envs RUN mix release # ... existing Docker stuff cont'd... ``` -------------------------------- ### Dotenvy.Parser.parse/3 Source: https://dotenvy.hexdocs.pm/Dotenvy.Parser.html Parses the provided .env file contents, substituting and merging them with existing variables. Supports custom system command execution via options. ```APIDOC ## Dotenvy.Parser.parse(contents, vars \\ %{}, opts \\ []) ### Description Parses the given `contents`, substituting and merging with the given `vars`. ### Parameters - **contents** (String) - Required - The raw content of the .env file. - **vars** (Map) - Optional - A map of existing variables to merge with the parsed results. - **opts** (Keyword List) - Optional - Configuration options for the parser. ### Options - **:sys_cmd_fn** (Function) - An arity 3 function returning a tuple matching the spec for System.cmd/3. Defaults to System.cmd/3. - **:sys_cmd_opts** (Keyword List) - Options passed as the 3rd argument to the :sys_cmd_fn. ### Example ```elixir # Disabling system commands Dotenvy.Parser.parse(contents, %{}, sys_cmd_fn: fn _cmd, _args, _opts -> {"", 0} end) ``` ``` -------------------------------- ### Validate variable names Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Regex pattern for valid environment variable names. ```regex [a-zA-Z_]+[a-zA-Z0-9_]* ``` -------------------------------- ### Define multi-line values Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Use triple-quoted heredoc syntax for multi-line strings like private keys. ```text PRIVATE_KEY=""" -----BEGIN RSA PRIVATE KEY----- ... HkVN9... ... -----END DSA PRIVATE KEY----- """ ``` -------------------------------- ### Configure build_per_environment in mix.exs Source: https://dotenvy.hexdocs.pm/minimal.html Set build_per_environment to false in the project configuration to reuse compiled artifacts across environments. ```elixir def project do [ app: :my_app, version: "0.1.0", elixir: "~> 1.18", start_permanent: Mix.env() == :prod, deps: deps(), releases: releases(), build_per_environment: false ] end ``` -------------------------------- ### env!(variable, type, default) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Reads an environment variable and converts its output, or returns a provided default value if the variable is missing. ```APIDOC ## env!(variable, type, default) ### Description Reads an env variable and converts its output or returns a default value. ### Parameters - **variable** (String) - Required - The name of the environment variable to read. - **type** (Atom) - Required - The type to convert the value to. - **default** (Any) - Required - The value to return if the environment variable is not set. ``` -------------------------------- ### Handling missing environment variables Source: https://dotenvy.hexdocs.pm/getting_started.html The error message displayed when a required environment variable is missing from the contract. ```text ** (RuntimeError) Environment variable PASSWORD not set ``` -------------------------------- ### Defining variables in env files Source: https://dotenvy.hexdocs.pm/getting_started.html Setting a required variable within an environment configuration file. ```text PASSWORD=xxxx ``` -------------------------------- ### 1Password URI Syntax Source: https://dotenvy.hexdocs.pm/1password.html The standard URI format for referencing specific fields within a 1Password vault item. ```text op:////[section-name/] ``` -------------------------------- ### Configuring non-strict string conversion Source: https://dotenvy.hexdocs.pm/getting_started.html Shows the configuration change to allow empty values by using the :string type instead of :string!. ```elixir config :example, :password, env!("PASSWORD", :string) ``` -------------------------------- ### Configure Phoenix Production Settings Source: https://dotenvy.hexdocs.pm/phoenix.html Defines compile-time configuration for the Phoenix endpoint and logger in production. ```elixir import Config # Compile-time configuration includes code_reloader, debug_errors, and force_ssl # https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-compile-time-configuration config :your_app, YourAppWeb.Endpoint, code_reloader: false, debug_errors: false, force_ssl: false # Do not print debug messages in production config :logger, level: :info ``` -------------------------------- ### Read and convert environment variables with env!/2 Source: https://dotenvy.hexdocs.pm/Dotenvy.html Reads an environment variable and converts it to a specified type, raising an error if conversion fails. ```elixir @spec env!(variable :: binary(), type :: Dotenvy.Transformer.conversion_type()) :: any() | no_return() ``` ```elixir iex> env!("PORT", :integer) 5432 iex> env!("ENABLED", :boolean) true ``` -------------------------------- ### Define environment variables in envs/dev.env Source: https://dotenvy.hexdocs.pm/cheatsheet.html Store non-sensitive defaults in a dedicated envs directory. ```text API_KEY=xyz AWS_REGION="us-east-1" HTTP_CLIENT=HTTPoison ``` -------------------------------- ### Resolve Environment Directory Path Source: https://dotenvy.hexdocs.pm/releases.html Use the RELEASE_ROOT environment variable or a relative path to determine the absolute location of the environment configuration directory. ```elixir env_dir_prefix = System.get_env("RELEASE_ROOT") || Path.expand("./envs") ``` -------------------------------- ### Accessing 1Password Secrets in .env Files Source: https://dotenvy.hexdocs.pm/1password.html Use the $() syntax to execute the op command directly within .env files for Dotenvy version 1.0.0 and later. ```bash DB_PASSWORD=$(op read op://MyVault/FooDatabase/password); API_KEY=$(op read op://MyVault/ImportantAPI/key); ``` -------------------------------- ### Cherry-pick system variables Source: https://dotenvy.hexdocs.pm/Dotenvy.html Explicitly passing specific system variables to be used for substitution. ```elixir iex> Dotenvy.source([ %{"HOME" => System.get_env("HOME")}, ".env", System.get_env() ]) ``` -------------------------------- ### Implement parse callback Source: https://dotenvy.hexdocs.pm/Dotenvy.html Defines the callback signature for custom parser implementations. ```elixir @callback parse(contents :: binary(), vars :: map(), opts :: keyword()) :: {:ok, map()} | {:error, any()} ``` -------------------------------- ### Export parsed variables to system environment Source: https://dotenvy.hexdocs.pm/Dotenvy.html Using System.put_env/1 to make parsed variables available globally in the application. ```elixir iex> {:ok, parsed_vars} = Dotenvy.source([".env", System.get_env()]) iex> System.put_env(parsed_vars) ``` -------------------------------- ### Accessing environment variables via System Source: https://dotenvy.hexdocs.pm/getting_started.html Demonstrates that Dotenvy does not export variables to the system environment. ```elixir iex> System.get_env("SECRET") nil ``` -------------------------------- ### env!(variable, type, default) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Reads an environment variable and converts its value to the specified type, or returns a provided default value if the variable is not set. ```APIDOC ## env!(variable, type, default) ### Description Reads an env variable and converts its output or returns a default value. The default value is returned as-is without conversion, and is only used when the variable is not set. ### Parameters - **variable** (binary) - Required - The name of the environment variable to read. - **type** (Dotenvy.Transformer.conversion_type) - Required - The target type for conversion. - **default** (any) - Required - The value to return if the variable is not set. ### Example ```elixir env!("PORT", :integer, 5432) # Returns: 5433 ``` ``` -------------------------------- ### Restrict system commands in Dotenvy.Parser Source: https://dotenvy.hexdocs.pm/Dotenvy.Parser.html Provide a custom function to the :sys_cmd_fn option to whitelist specific commands and raise an error for unauthorized attempts. ```elixir iex> Dotenvy.Parser.parse(contents, %{}, sys_cmd_fn: fn "op", args, opts -> System.cmd("op", args, opts) _cmd, _args, _opts -> raise "Command not allowed" end) ``` -------------------------------- ### env!(variable, type \ :string) Source: https://dotenvy.hexdocs.pm/Dotenvy.html Reads the specified environment variable and converts its value to the requested type. ```APIDOC ## env!(variable, type \ :string) ### Description Reads the given env variable and converts its value to the given type. ### Parameters - **variable** (String) - Required - The name of the environment variable to read. - **type** (Atom) - Optional - The type to convert the value to (defaults to :string). ``` -------------------------------- ### Define input_source type Source: https://dotenvy.hexdocs.pm/Dotenvy.html Defines the accepted input types for environment sources, supporting file paths or maps. ```elixir @type input_source() :: String.t() | %{optional(String.t()) => String.t()} ``` -------------------------------- ### Execute command substitution Source: https://dotenvy.hexdocs.pm/dotenv-file-format.html Use $(command) syntax to inject output from shell commands into variables. ```bash API_KEY=$(op read op://MyVault/SomeService/api_key) ``` -------------------------------- ### Define source! function specification Source: https://dotenvy.hexdocs.pm/Dotenvy.html This specification defines the input types and return behavior for the source! function. ```elixir @spec source!(files :: binary() | [binary()], opts :: keyword()) :: %{optional(String.t()) => String.t()} | no_return() ``` -------------------------------- ### Define source/2 specification Source: https://dotenvy.hexdocs.pm/Dotenvy.html The function signature and return type specification for source/2. ```elixir @spec source(inputs :: input_source() | [input_source()], opts :: keyword()) :: {:ok, %{optional(String.t()) => String.t()}} | {:error, any()} ``` -------------------------------- ### Read environment variables with default values using env!/3 Source: https://dotenvy.hexdocs.pm/Dotenvy.html Reads an environment variable with a fallback default value. The default value is returned as-is without conversion. ```elixir @spec env!( variable :: binary(), type :: Dotenvy.Transformer.conversion_type(), default :: any() ) :: any() | no_return() ``` ```elixir iex> env!("PORT", :integer, 5432) 5433 iex> env!("NOT_SET", :boolean, %{not: "converted"}) %{not: "converted"} iex> System.put_env("HOST", "") iex> env!("HOST", :string!, "localhost") ** (RuntimeError) Error converting HOST to string!: non-empty value required ``` -------------------------------- ### Apply custom transformation function Source: https://dotenvy.hexdocs.pm/cheatsheet.html Transform environment strings into complex Elixir types using custom functions. ```text PHX_IP="0, 0, 0, 0, 0, 0, 0, 0" ``` ```elixir config :feenix, FeenixWeb.Endpoint, http: [ # Enable IPv6 and bind on all interfaces. ip: env!("PHX_IP", fn ip -> ip |> String.split(",") |> Enum.map(&String.trim/1) |> Enum.map(&String.to_integer/1) |> List.to_tuple() end) ], ```