Configuration
Configure go-migration using YAML, JSON, or environment variables — complete reference with all supported fields and default values.
Configuration
go-migration supports configuration through YAML files, JSON files, and environment variables. When using migrator.Run(), the config file defaults to migration.json in the current directory. You can also specify a path with the --config flag.
# Uses migration.json in the current directory (default)
go-migration migrate
# Specify a custom config file
go-migration migrate --config=./config/production.json
go-migration migrate --config=./config/migration.yamlConfiguration format: The default configuration file is migration.json (JSON). The format is auto-detected from the file extension. YAML files (.yaml/.yml) are still supported via the --config flag, but YAML is deprecated — when a YAML file is loaded, go-migration prints a deprecation warning to stderr. Migrate to JSON.
Configuration File
The configuration file defines your database connections, pool settings, migration table name, and logging preferences. All connection details live under a named entry inside the connections map; there are no top-level driver/host/port fields.
Create a migration.json file in your project root:
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "localhost",
"port": 5432,
"database": "myapp",
"username": "dbuser",
"password": "dbpass",
"max_open_conns": 25,
"max_idle_conns": 5,
"conn_max_lifetime": "5m",
"options": {
"sslmode": "disable"
}
}
},
"migration_table": "migrations",
"log_level": "info"
}Create a migration.yaml file in your project root:
# Default connection name
default: postgres
connections:
postgres:
# Database driver: postgres, mysql, or sqlite
driver: postgres
# Connection details
host: localhost
port: 5432
database: myapp
username: dbuser
password: dbpass
# Connection pool settings
max_open_conns: 25
max_idle_conns: 5
conn_max_lifetime: 5m # duration string
# Driver-specific options (e.g. PostgreSQL sslmode)
options:
sslmode: disable
# Migration settings (apply globally)
migration_table: migrations
# Logging
log_level: infoSupported Fields
Top-Level Keys
These are the only keys recognized at the top level of the config file:
| Field | Type | Required | Description |
|---|---|---|---|
connections | map | Yes | Map of named database connections (see Multi-Connection Configuration) |
default | string | No | Name of the default connection to use |
migration_table | string | No | Name of the migrations tracking table |
migration_dir | string | No | Directory for migration files (default: database/migrations) |
seeder_dir | string | No | Directory for seeder files (default: database/seeders) |
factory_dir | string | No | Directory for factory files (default: database/factories) |
log_level | string | No | Logging verbosity — debug, info, warn, error |
log_output | string | No | Log destination — console, file, or both |
Per-Connection Fields
Each entry under connections.<name> accepts these fields:
| Field | Type | Required | Description |
|---|---|---|---|
driver | string | Yes | Database driver — postgres, mysql, or sqlite |
host | string | Yes | Database server hostname or IP address |
port | int | No | Database server port |
database | string | Yes | Database name (or file path for SQLite) |
username | string | No | Database user |
password | string | No | Database password |
max_open_conns | int | No | Maximum number of open connections |
max_idle_conns | int | No | Maximum number of idle connections in the pool |
conn_max_lifetime | duration | No | Maximum connection lifetime as a Go duration string (e.g. "5m", "300s") |
options | map | No | Driver-specific connection options, e.g. {"sslmode": "disable"} for PostgreSQL |
conn_max_lifetime is a Go time.Duration. There is no custom unmarshaler, so a bare number (e.g. 300) is interpreted as nanoseconds, not seconds. Always use a duration string like "5m" or "300s".
Driver-specific parameters such as PostgreSQL's sslmode are not top-level fields. Put them inside the connection's options map. They are appended to the generated DSN.
Environment Variable Fallback
go-migration can build an entire configuration from environment variables. This is a wholesale fallback: when the CLI fails to load the config file (for example, the file does not exist), it falls back to LoadFromEnv(), which constructs a single connection named after GOMIGRATE_DEFAULT_CONNECTION (default default). This is distinct from ${VAR} interpolation, which substitutes individual values inside a config file that does load.
Naming Convention
All recognized environment variables use the GOMIGRATE_ prefix. Connection fields use the GOMIGRATE_DB_ prefix:
| Config Field | Environment Variable |
|---|---|
connection driver | GOMIGRATE_DB_DRIVER |
connection host | GOMIGRATE_DB_HOST |
connection port | GOMIGRATE_DB_PORT |
connection database | GOMIGRATE_DB_DATABASE |
connection username | GOMIGRATE_DB_USERNAME |
connection password | GOMIGRATE_DB_PASSWORD |
default | GOMIGRATE_DEFAULT_CONNECTION |
There is no environment variable for sslmode or other driver options when loading from env. LoadFromEnv() only reads the fields listed above.
Directory and Logging Settings
LoadFromEnv() also reads these top-level settings from GOMIGRATE_-prefixed variables:
| Config Field | Environment Variable | Default |
|---|---|---|
migration_dir | GOMIGRATE_MIGRATION_DIR | database/migrations |
seeder_dir | GOMIGRATE_SEEDER_DIR | database/seeders |
factory_dir | GOMIGRATE_FACTORY_DIR | database/factories |
migration_table | GOMIGRATE_MIGRATION_TABLE | migrations |
log_level | GOMIGRATE_LOG_LEVEL | info |
log_output | GOMIGRATE_LOG_OUTPUT | console |
Resolution Order
The CLI resolves configuration in this order:
- Configuration file —
config.Load()reads and parses the file (with${VAR}interpolation applied first). - Environment fallback — if the file cannot be loaded,
config.LoadFromEnv()builds the config entirely fromGOMIGRATE_*variables. - Defaults —
ApplyDefaults()fills in built-in defaults for any optional field still empty.
# Build the whole config from the environment (used when no config file loads)
export GOMIGRATE_DB_DRIVER=postgres
export GOMIGRATE_DB_HOST=prod-db.example.com
export GOMIGRATE_DB_PORT=5432
export GOMIGRATE_DB_DATABASE=myapp_production
export GOMIGRATE_DB_USERNAME=app_user
export GOMIGRATE_DB_PASSWORD=s3cretThe environment fallback is all-or-nothing for the file: it kicks in only when the config file fails to load. It is not a per-field fallback layered on top of a successfully loaded file. For per-value substitution inside a loaded file, use ${VAR} interpolation instead.
Avoid committing passwords and secrets to version control. Use environment variables or a .env file (excluded from git) for sensitive values.
Default Values
go-migration's ApplyDefaults() fills in these top-level defaults when they are not specified:
| Setting | Default Value | Description |
|---|---|---|
migration_table | migrations | Name of the table that tracks applied migrations |
migration_dir | database/migrations | Directory for migration files |
seeder_dir | database/seeders | Directory for seeder files |
factory_dir | database/factories | Directory for factory files |
log_level | info | Logging verbosity level |
log_output | console | Log destination |
Pool Settings
go-migration sets no pool defaults of its own. It only calls SetMaxOpenConns, SetMaxIdleConns, and SetConnMaxLifetime when the corresponding config value is greater than zero; otherwise the underlying *sql.DB keeps Go's database/sql defaults.
| Setting | Effective When Unset | Source |
|---|---|---|
max_open_conns | unlimited (0) | Go database/sql default |
max_idle_conns | 2 | Go database/sql default — not set by go-migration |
conn_max_lifetime | no limit (0) | Go database/sql default |
For PostgreSQL, the driver defaults sslmode to disable when no sslmode is present in the connection's options map. To change it, set options.sslmode. Pool settings map directly to Go's *sql.DB methods — see Pool Configuration for tuning recommendations.
Driver-Specific Examples
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "localhost",
"port": 5432,
"database": "myapp",
"username": "postgres",
"password": "secret",
"max_open_conns": 25,
"max_idle_conns": 5,
"conn_max_lifetime": "5m",
"options": { "sslmode": "disable" }
}
},
"migration_table": "migrations",
"log_level": "info"
}{
"default": "mysql",
"connections": {
"mysql": {
"driver": "mysql",
"host": "localhost",
"port": 3306,
"database": "myapp",
"username": "root",
"password": "secret",
"max_open_conns": 25,
"max_idle_conns": 5,
"conn_max_lifetime": "5m"
}
},
"migration_table": "migrations",
"log_level": "info"
}{
"default": "sqlite",
"connections": {
"sqlite": {
"driver": "sqlite",
"database": "./data/myapp.db"
}
},
"migration_table": "migrations",
"log_level": "info"
}SQLite uses the database field as the file path. The host, port, username, and password fields are not required. Note that the SQLite driver registers itself under the name sqlite3 for database/sql, while config validation accepts sqlite as the driver value.
Multi-Connection Configuration
The default field specifies which connection is used when no specific connection is requested. The connections field is a map of named database connections, where each key is a connection name and the value contains the connection settings. Every config file uses this structure — connection details always live under connections.<name>.
Directory and table settings (migration_dir, seeder_dir, factory_dir, migration_table, log_level) are defined at the top level and apply globally across all connections.
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "${DB_HOST}",
"port": 5432,
"database": "${DB_DATABASE}",
"username": "${DB_USERNAME}",
"password": "${DB_PASSWORD}",
"options": { "sslmode": "disable" }
},
"mysql": {
"driver": "mysql",
"host": "${MYSQL_HOST}",
"port": 3306,
"database": "${MYSQL_DATABASE}",
"username": "${MYSQL_USERNAME}",
"password": "${MYSQL_PASSWORD}"
}
},
"migration_dir": "database/migrations",
"seeder_dir": "database/seeders",
"factory_dir": "database/factories",
"migration_table": "migrations",
"log_level": "info"
}# Default connection name
default: postgres
# Multiple database connections
connections:
postgres:
driver: postgres
host: ${DB_HOST}
port: 5432
database: ${DB_DATABASE}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
options:
sslmode: disable
mysql:
driver: mysql
host: ${MYSQL_HOST}
port: 3306
database: ${MYSQL_DATABASE}
username: ${MYSQL_USERNAME}
password: ${MYSQL_PASSWORD}
# Directory and settings (apply globally)
migration_dir: database/migrations
seeder_dir: database/seeders
factory_dir: database/factories
migration_table: migrations
log_level: infoWhen using the --config flag, the structure works the same way:
go-migration migrate --config=./config/migration.jsonThere is no "flat" configuration format. Connection details (driver, host, port, etc.) must be nested under connections.<name>. Placing them at the top level has no effect, since the Config struct only recognizes connections, default, migration_table, migration_dir, seeder_dir, factory_dir, log_level, and log_output at the top level.
Environment Variable Interpolation
You can embed environment variable references directly in your YAML or JSON config values using ${VAR_NAME} placeholders. This is different from the environment variable fallback — interpolation lets you compose values from multiple environment variables inside a config file that loads successfully.
default: postgres
connections:
postgres:
driver: postgres
host: ${DB_HOST}
port: 5432
database: ${DB_NAME}
username: ${DB_USER}
password: ${DB_PASSWORD}
options:
sslmode: ${DB_SSLMODE}
log_level: infoHow It Works
Interpolation happens after reading the file and before YAML/JSON parsing:
- go-migration reads the raw config file content
- All
${VAR_NAME}placeholders are replaced with the corresponding environment variable values - The resulting string is then parsed as YAML or JSON
Auto-Unquote for JSON (v1.0.0)
When using JSON config files, InterpolateEnv automatically removes surrounding quotes for values that resolve to numeric, boolean (true/false), or null types. This means your JSON stays valid with correct types after interpolation.
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "${DB_HOST}",
"port": "${DB_PORT}",
"database": "${DB_NAME}",
"options": { "sslmode": "disable" }
}
}
}With DB_HOST=localhost, DB_PORT=5432, and DB_NAME=myapp, the result after interpolation is:
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "localhost",
"port": 5432,
"database": "myapp",
"options": { "sslmode": "disable" }
}
}
}Notice that "port": "${DB_PORT}" becomes "port": 5432 (a number, not the string "5432"). The same applies to boolean and null values:
| Environment Variable | JSON Before | JSON After |
|---|---|---|
DB_PORT=5432 | "port": "${DB_PORT}" | "port": 5432 |
ENABLE_SSL=true | "ssl": "${ENABLE_SSL}" | "ssl": true |
EXTRA=null | "extra": "${EXTRA}" | "extra": null |
DB_HOST=localhost | "host": "${DB_HOST}" | "host": "localhost" |
Auto-unquote applies only to JSON files. In YAML files, data types are handled natively by the YAML parser, so no unquoting is needed.
Escaping
To produce a literal ${VAR_NAME} in your config (without interpolation), use the double-dollar escape sequence:
connections:
postgres:
# This resolves to the value of DB_HOST
host: ${DB_HOST}
# This produces the literal string "${NOT_REPLACED}"
password: $${NOT_REPLACED}Missing Variables
If any ${VAR_NAME} references an environment variable that is not set, go-migration returns an error listing all unresolved variables. This prevents silent misconfiguration.
All unresolved ${VAR_NAME} placeholders are reported in a single error message, so you can fix them all at once rather than one at a time.
Config Validation
go-migration validates your configuration at load time and reports all violations in a single error. This catches common mistakes before any database operations are attempted.
Validated Fields
| Field | Rule |
|---|---|
driver | Must be postgres, mysql, or sqlite |
port | Must be a positive number when specified |
log_level | Must be debug, info, warn, or error |
log_output | Must be console, file, or both |
max_open_conns | Must be non-negative |
max_idle_conns | Must be non-negative |
Example Error
If multiple fields are invalid, you get a single error with all violations joined together:
configuration validation failed: connections.postgres.driver must be one of: postgres, mysql, sqlite, log_level must be one of: debug, info, warn, errorValidation runs after environment variable interpolation and fallback resolution, so the final resolved values are what gets validated.
Programmatic Usage
The config package is publicly available at pkg/config, so you can import it directly in your own Go projects — no need to reimplement config loading, validation, or environment variable interpolation.
package main
import (
"fmt"
"log"
"github.com/gopackx/go-migration/pkg/config"
)
func main() {
// Load configuration from a JSON (or deprecated YAML) file
cfg, err := config.Load("migration.json")
if err != nil {
log.Fatalf("failed to load config: %v", err)
}
// Apply default values for optional fields
cfg.ApplyDefaults()
// Validate the resolved configuration
if err := cfg.Validate(); err != nil {
log.Fatalf("invalid config: %v", err)
}
// Connection details live under the connections map.
conn := cfg.Connections[cfg.DefaultConn]
fmt.Printf("Connected to %s on %s:%d\n", conn.Database, conn.Host, conn.Port)
}config.Load() handles file reading, YAML/JSON parsing, and environment variable interpolation (${VAR_NAME}) in a single call. Follow it with ApplyDefaults() and Validate() for a fully resolved and validated configuration.
What's Next?
- Framework Integration — use go-migration with Gin, Echo, Fiber, or net/http
- CLI Reference — run migrations from the command line
- Error Handling — diagnose and handle migration errors