--- title: Configuration description: Read layered configuration with compass, from plugin defaults through per-environment files and SUMMER_ variables to runtime overrides saved to disk. section: services order: 10 --- # Configuration WinterCMS reads configuration with `Config::get('app.name')` from `config/*.php`, per-environment directories and `.env`. SummerCMS reads it from a `compass.Config` built by [compass](../../modules/compass/README.md), with the same dot paths. [Setup, Configuration](../setup/configuration.md) lists the keys an application sets; this page covers how the layers merge and how code reads and changes them. ## Layers `compass.Config` merges its sources in a fixed order. Each layer overrides the ones before it: 1. Plugin defaults, merged with `compass.Config.MergePlugin` when the plugin is activated. A plugin's `config/config.yaml` becomes `.`, so `acme.blog.posts_per_page` is the WinterCMS `acme.blog::posts_per_page`. Any other `config/.yaml` becomes `..`. 2. `config/*.yaml`. Each file is a section named after the file, so `config/app.yaml` provides `app.*`. 3. `config/env//*.yaml`, the per-environment sections. 4. `SUMMER_` environment variables and the `.env` file next to `config/`. `SUMMER_MAIL__DRIVER` sets `mail.driver`: the prefix is removed, `__` separates the path segments and the name is lower-cased. A `.env` value applies only when the real environment does not set the same variable. 5. `config/env//overrides.yaml`, written by `compass.Config.Persist`. 6. Values set in memory with `compass.Config.Set`. The environment comes from `SUMMER_ENV` and defaults to `production`. Its name may contain only letters, digits, `-` and `_`. ## Reading values The typed getters `compass.Config.String`, `compass.Config.Int` and `compass.Config.Bool` return the zero value for a missing key. Use `compass.Config.Lookup` or `compass.Config.Has` when a missing key must be told apart from a zero value, and `compass.Config.LoadSection` to read a whole section into a struct with `koanf` tags: ```go src=modules/compass/example_test.go#ExampleOpen dir, err := os.MkdirTemp("", "acme-config") if err != nil { fmt.Println(err) return } defer os.RemoveAll(dir) if err := writeConfig(dir); err != nil { fmt.Println(err) return } // Environ stands in for the process environment (nil reads os.Environ). cfg, err := compass.Open(compass.Options{ Dir: dir, Env: "development", Environ: []string{"SUMMER_MAIL__DRIVER=smtp"}, }) if err != nil { fmt.Println(err) return } // A plugin's embedded config/config.yaml becomes its defaults. plugin := fstest.MapFS{"config/config.yaml": {Data: []byte("posts_per_page: 10\n")}} if err := cfg.MergePlugin("acme.blog", plugin); err != nil { fmt.Println(err) return } fmt.Println(cfg.String("app.name"), cfg.Bool("app.debug"), cfg.Int("acme.blog.posts_per_page")) var mail mailSettings if err := cfg.LoadSection("mail", &mail); err != nil { fmt.Println(err) return } fmt.Println(mail.Driver, mail.From) _, found := cfg.Lookup("app.timezone") fmt.Println(cfg.Environment(), found) // A runtime override, saved to env/development/overrides.yaml. if err := cfg.Set("acme.blog.posts_per_page", 25); err != nil { fmt.Println(err) return } if err := cfg.Persist(); err != nil { fmt.Println(err) return } saved, _ := os.ReadFile(filepath.Join(dir, "env", "development", "overrides.yaml")) fmt.Print(string(saved)) // Output: // Acme true 10 // smtp blog@example.com // development false // acme: // blog: // posts_per_page: 25 ``` The application opens its configuration once with `compass.Load("config")` in the generated `main` and hands it to the `backpack.App`. Plugins read it through `app.Config` and never open their own. ## Changing values at runtime `compass.Config.Set` changes a value in memory. `compass.Config.Persist` saves every value set at runtime to `config/env//overrides.yaml`, keeping the keys already saved there. It replaces the file atomically, creates it readable only by its owner and refuses any path outside the config directory. `compass.Config.Reload` rereads every source and discards values that were set but not persisted. Values that admins edit in the backend are not configuration: settings pages store them in a database row. > [!WARNING] > `overrides.yaml` is written by the running application. Keep it out of version control, and keep secrets in environment variables rather than in values the application persists.