Skip to content

Configuration

devituz edited this page Jun 3, 2026 · 1 revision

Configuration

lagodev reads two distinct configuration sources:

Source What it controls
.env files Runtime database connection, logging, app environment
lago.json Directory layout the CLI uses for generators

These never overlap — .env is about what the running program does, lago.json is about where files land on disk when generators run.

.env

Generate a starter file:

lago env:init       # writes ./.env
# .env
APP_ENV=local
APP_DEBUG=true
APP_NAME=app

# Driver: postgres | mysql | sqlite
DB_CONNECTION=postgres
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=secret
DB_DATABASE=app
DB_SCHEMA=public
DB_SSL_MODE=disable
DB_TIMEZONE=Asia/Tashkent

# Pool tuning (optional)
DB_MAX_OPEN=25
DB_MAX_IDLE=5
DB_CONN_MAX_LIFETIME=1h
DB_CONN_MAX_IDLE_TIME=10m

# Logging (optional)
DB_LOG_QUERIES=true
DB_SLOW_QUERY=200ms

Load order

The CLI loads .env files in this order — later files overlay earlier ones:

  1. .env — shared / committed
  2. .env.local — developer overrides (gitignored)
  3. .env.$APP_ENV — environment-specific (.env.production, .env.testing, …)

os.Getenv always wins over .env files — the process environment is the final source of truth. This means CI / production secrets injected through your platform of choice (Kubernetes secrets, Docker env, AWS Parameter Store, etc.) override anything checked into the repo.

Reading from your own code

import "github.com/devituz/lagodev/config"

func init() { _ = config.LoadEnv() }

cfg   := config.FromEnv()                 // builds a database.Config
debug := config.Bool("APP_DEBUG", false)
host  := config.String("REDIS_HOST", "localhost")
ttl   := config.Duration("CACHE_TTL", 5*time.Minute)
port  := config.Int("APP_PORT", 8080)

config.LoadEnv() is safe to call from init() — it's idempotent and returns nil if no .env files exist.

Inspecting the active environment

lago env                       # password/secret/key/token redacted
lago env --show-secrets        # full output

Reference

Variable Type Default Notes
APP_ENV string "" Selects the third .env.$APP_ENV overlay; lagodev also reads it to hide error details when =production
APP_DEBUG bool false Free for your app to read
APP_NAME string "" Free for your app to read
APP_KEY string "" AES-256 key (base64). Generated by lago key:generate
DB_CONNECTION string sqlite postgres, mysql, sqlite
DB_DSN string "" Overrides the structured fields below
DB_HOST / DB_PORT string/int (driver default)
DB_USERNAME / DB_PASSWORD string ""
DB_DATABASE / DB_SCHEMA string ""
DB_SSL_MODE string "" Postgres: disable, require, verify-ca, …
DB_TIMEZONE string UTC IANA name (Asia/Tashkent); also accepts Local
DB_MAX_OPEN int (driver default) Pool tuning
DB_MAX_IDLE int (driver default)
DB_CONN_MAX_LIFETIME duration 0 1h, 30m, etc.
DB_CONN_MAX_IDLE_TIME duration 0
DB_LOG_QUERIES bool false Log every SQL call
DB_SLOW_QUERY duration 0 Log queries slower than this
LAGO_CONFIG path lago.json Override the per-project config path

lago.json

Each project decides where models, migrations, factories, seeders, and tests live. Generate the file:

lago init       # writes ./lago.json with the default layout
{
  "paths": {
    "models":     "models",
    "migrations": "migrations",
    "factories":  "factories",
    "seeders":    "seeders",
    "tests":      "tests"
  }
}

Want everything under internal/?

{
  "paths": {
    "models":     "internal/domain",
    "migrations": "internal/db/migrations",
    "factories":  "internal/testdata/factories",
    "seeders":    "internal/testdata/seeders",
    "tests":      "internal/db/tests"
  }
}

The make:* commands honor these paths automatically; pass --dir to override on a per-command basis.

Resolution order

  1. The path in $LAGO_CONFIG (if set)
  2. lago.json in the current working directory
  3. Built-in defaults (the table above)

A malformed lago.json falls back to defaults with a warning on stderr — so a typo can't take your generators offline.

Time zones

Set DB_TIMEZONE (or Config.TimeZone) to an IANA name and lagodev:

  • adds _loc=... to the SQLite DSN so timestamps round-trip in your zone,
  • adds loc=... to the MySQL DSN,
  • adds timezone=... to the Postgres DSN session,
  • uses time.Now().In(loc) for CreatedAt / UpdatedAt.
conn.Location()    // *time.Location
conn.Now()         // time.Now().In(conn.Location())

The default is UTC. Most production deployments should stay on UTC and display in the user's zone at the edge — but lagodev gives you the lever when you need it (e.g. legacy DBs storing local time).

Production-safe error responses

When APP_ENV=production, the web framework's InternalError / Error helpers strip the underlying err.Error() from the response body and return a generic "internal server error" payload. The raw error still hits the Recovery middleware's log so you can debug it — it just doesn't leak DB internals to clients.

See Web-Framework for the full secure-by-default story.

See also

Clone this wiki locally