Skip to content

config.yml Detailed Explanation and Recommended Configuration

Last Updated: 2026-03-28

1. Configuration Loading Rules

When the backend starts, values are taken in the following order:

  1. Use built-in default values
  2. Read config.yml
  3. Read environment variables to override (e.g., server.portSERVER_PORT)

2. Conclusion First: Database Selection Recommendations

  • Development Environment: Prefer sqlite (simple deployment, zero dependencies)
  • Production Environment: Prefer postgres (better concurrency, reliability, and observability)

If you use sqlite in production, you must accept:

  • Weak write concurrency
  • Strong binding to single machine/disk
  • Limited horizontal scaling and high availability

3. Copyable Demo Configurations

3.1 Demo A: Local Development (SQLite)

Applicable Scenarios: Single-machine development, low concurrency testing.

yaml
server:
  host: 0.0.0.0
  port: 8080
  mode: debug

log:
  dir: ""
  filename: app.log
  max_size_mb: 100
  max_backups: 7
  max_age_days: 30
  compress: true

database:
  driver: sqlite
  dsn: ./db/dujiao.db?_busy_timeout=5000&_journal_mode=WAL&_synchronous=NORMAL
  pool:
    max_open_conns: 1
    max_idle_conns: 1
    conn_max_lifetime_seconds: 0
    conn_max_idle_time_seconds: 0

jwt:
  secret: "dev-admin-jwt-secret-change-me-please-32chars"
  expire_hours: 24

user_jwt:
  secret: "dev-user-jwt-secret-change-me-please-32chars"
  expire_hours: 24
  remember_me_expire_hours: 168

SQLite Key Reminders:

  • It is recommended to set max_open_conns to 1, otherwise database is locked errors may occur during high-concurrency writes.
  • _journal_mode=WAL can improve read-write concurrency experience (commonly used on a single machine).
  • It is not recommended to place the SQLite data file on an unstable network drive (may cause lock exceptions).

3.2 Demo B: Production Environment (PostgreSQL)

Applicable scenarios: official business, predictable concurrent traffic.

yaml
server:
  host: 0.0.0.0
  port: 8080
  mode: release

log:
  dir: /var/log/dujiao-next
  filename: app.log
  max_size_mb: 100
  max_backups: 14
  max_age_days: 30
  compress: true

database:
  driver: postgres
  dsn: host=127.0.0.1 port=5432 user=dujiao password=CHANGE_ME dbname=dujiao sslmode=disable TimeZone=Asia/Shanghai
  pool:
    max_open_conns: 50
    max_idle_conns: 10
    conn_max_lifetime_seconds: 1800
    conn_max_idle_time_seconds: 600

jwt:
  secret: "replace-with-strong-random-admin-secret-64chars"
  expire_hours: 24

user_jwt:
  secret: "replace-with-strong-random-user-secret-64chars"
  expire_hours: 24
  remember_me_expire_hours: 168

PostgreSQL Key Reminders:

  • Do not set max_open_conns higher than PostgreSQL's max_connections limit
  • It's recommended to reserve some connections for DBA/monitoring/migration tasks to prevent the business from exhausting the connection pool
  • It's recommended to set conn_max_lifetime_seconds to avoid occasional errors caused by long-lived connections being reclaimed by intermediate network devices
  • It's recommended to explicitly configure TimeZone to prevent order times and log times from being misaligned

3.3 Demo C: Low-Traffic Production (PostgreSQL Low Resource)

Applicable Scenarios: Lightweight business, low-spec cloud hosts.

yaml
database:
  driver: postgres
  dsn: host=127.0.0.1 port=5432 user=dujiao password=CHANGE_ME dbname=dujiao sslmode=disable TimeZone=Asia/Shanghai
  pool:
    max_open_conns: 20
    max_idle_conns: 5
    conn_max_lifetime_seconds: 1200
    conn_max_idle_time_seconds: 300

4. How to Adjust Connection Pool Parameters

The following recommendations apply to general scenarios in the current project (API, backend operations, payment callbacks).

  • max_open_conns
    • Meaning: Maximum number of simultaneously open connections
    • SQLite recommendation: 1
    • PostgreSQL recommendation: 20~100 (adjust according to business volume and DB specs)
  • max_idle_conns
    • Meaning: Number of idle connections retained in the pool
    • Recommendation: Usually set to 20%~40% of max_open_conns
  • conn_max_lifetime_seconds
    • Meaning: Maximum lifetime of a single connection
    • Recommendation: 900~3600; 0 means no limit
  • conn_max_idle_time_seconds
    • Meaning: Maximum idle time for idle connections
    • Recommendation: 300~1200; 0 means no limit

Common incorrect combinations:

  • max_idle_conns > max_open_conns (meaningless and potentially misleading)
  • Setting max_open_conns too high in PostgreSQL, causing too many clients errors
  • Setting max_open_conns to multiple connections in SQLite, leading to increased lock conflicts

5. Explanation of Group Fields

5.0 app

FieldTypeDefaultDescriptionRecommendation
secret_keystringchange-me-32-byte-secret-key!!AES-256 encryption key for encrypting payment keys, Bot Tokens and other sensitive dataMust be changed to a random 32-byte string

This key must not be changed after deployment, otherwise previously encrypted data will become unreadable.

5.1 server

FieldTypeDefaultDescriptionRecommendation
hoststring0.0.0.0Listening address0.0.0.0
portstring8080Service port8080
modestringdebugRunning mode: debug/releaseUse release for production

5.2 log

FieldTypeDefaultDescriptionRecommendation
dirstring""Log directory; if empty, logs in the running directory is usedExplicitly specify in production
filenamestringapp.logLog file nameapp.log
max_size_mbint100Maximum size per file in MB100
max_backupsint7Number of files to retain7~14
max_age_daysint30Retention period in days30
compressbooltrueWhether to compress archivestrue

5.3 database

FieldTypeDefaultDescriptionRecommendation
driverstringsqlitesqlite or postgresUse postgres in production
dsnstring./db/dujiao.dbDatabase connection stringConfigure according to environment
pool.max_open_connsint1Maximum open connectionsSQLite=1; Postgres=20~100
pool.max_idle_connsint1Maximum idle connections5~20 or 20%~40% of open connections
pool.conn_max_lifetime_secondsint0Maximum connection lifetime (seconds, 0=no limit)900~3600
pool.conn_max_idle_time_secondsint0Maximum idle connection lifetime (seconds, 0=no limit)300~1200

5.4 jwt / user_jwt

FieldTypeDefaultDescriptionRecommended
secretstringchange-me-in-productionSigning keyAt least a 32-character random string
expire_hoursint24Token expiration time (hours)24
remember_me_expire_hoursint168Used only by user_jwt, remember me expiration time168 (7 days)

5.5 redis

FieldTypeDefaultDescriptionRecommended
enabledbooltrueWhether to enable RedisRecommended true in production
hoststring127.0.0.1Redis addressSet according to environment
portint6379Redis port6379
passwordstring""Redis passwordMust be set in production
dbint0DB index0
prefixstringdjKey prefixdj or custom

5.6 queue

FieldTypeDefaultDescriptionRecommended
enabledbooltrueWhether to enable async queueRecommended true
hoststring127.0.0.1Queue Redis addressCan share but use a different DB from redis
portint6379Queue Redis port6379
passwordstring""Redis passwordMust be set in production
dbint1Queue DB index1
concurrencyint10Worker concurrency5~20
queuesmapdefault:10, critical:5Queue names and weightsAdjust as needed

Note: If queue.enabled=true but Redis is unreachable, asynchronous tasks (such as emails) may fail or pile up.

Additional notes:

  • The default startup mode is all (API + Worker).
  • If queue.enabled=false, start with -mode api; otherwise Worker initialization will fail.

5.7 upload

FieldTypeDescriptionRecommendation
max_sizeint64Maximum upload size (bytes)10485760 (10MB)
allowed_types[]stringAllowed MIME typesOnly necessary types
allowed_extensions[]stringAllowed file extensionsMatch with MIME types
max_width / max_heightintMaximum image dimensions4096

5.8 cors

FieldTypeDescriptionRecommendation
allowed_origins[]stringAllowed originsDo not use * in production
allowed_methods[]stringAllowed methodsKeep to a minimal set
allowed_headers[]stringAllowed request headersRetain according to business needs
allow_credentialsboolWhether to allow credentialsMatch frontend policy
max_ageintPreflight cache duration in seconds600

Additional notes:

  • Browser constraint: when allow_credentials=true, allowed_origins must not contain *.

5.9 security

FieldTypeDefaultDescriptionRecommended
login_rate_limit.window_secondsint300Rate limit detection window (seconds)300
login_rate_limit.max_attemptsint5Maximum failed attempts within window5
login_rate_limit.block_secondsint900Block duration when limit exceeded (seconds)900
password_policy.min_lengthint8Minimum password length8 or higher
password_policy.require_upperbooltrueWhether to require uppercase letterstrue
password_policy.require_lowerbooltrueWhether to require lowercase letterstrue
password_policy.require_numberbooltrueWhether to require digitstrue
password_policy.require_specialboolfalseWhether to require special charactersEnable as needed

5.10 email

FieldTypeDescriptionRecommended
enabledboolWhether email is enabledEnable as needed
host/portstring/intSMTP addressConfigure according to provider
username/passwordstringSMTP account password/authorization codeUse authorization code
from/from_namestringSender address and nameUse company domain email
use_tls/use_sslboolTransport security strategyChoose one, follow provider documentation
verify_code.*mixedVerification code validity, frequency, lengthDefault values commonly used

5.11 bootstrap

FieldTypeDescriptionRecommended
default_admin_usernamestringUsername for first-time default admin initializationSet it explicitly to your own admin username
default_admin_passwordstringPassword for first-time default admin initializationSet a strong password

Additional notes:

  • The default admin is only created when the admins table is empty.
  • Priority: DJ_DEFAULT_ADMIN_USERNAME / DJ_DEFAULT_ADMIN_PASSWORD (environment variables) > bootstrap.default_admin_username / bootstrap.default_admin_password (config.yml) > system defaults.
  • In release mode, if no admin password is provided in either environment variables or config.yml, default admin initialization will be skipped.

5.12 order

FieldTypeDefaultDescriptionRecommended
payment_expire_minutesint15Timeout in minutes for unpaid orders15~30

Additional notes:

  • The effective value may be overridden by admin settings (see "Runtime Override Priority" below).

5.13 telegram_auth (optional)

FieldTypeDescriptionRecommended
enabledboolEnable Telegram loginEnable when needed
bot_usernamestringBot username (without @)e.g. dujiao_login_bot
bot_tokenstringBot tokenGenerated by BotFather
login_expire_secondsintLogin validity (seconds)300
replay_ttl_secondsintReplay protection TTL (seconds)300

5.14 captcha (optional)

config.yml.example may not show this section completely, but it is supported by the system.

  • provider: none / image / turnstile
  • scenes:
    • login
    • register_send_code
    • reset_send_code
    • guest_create_order
    • gift_card_redeem
  • image: image captcha parameters
  • turnstile: Cloudflare Turnstile parameters

Example:

yaml
captcha:
  provider: turnstile
  scenes:
    login: true
    register_send_code: true
    reset_send_code: true
    guest_create_order: true
    gift_card_redeem: true
  turnstile:
    site_key: "<your-site-key>"
    secret_key: "<your-secret-key>"
    verify_url: "https://challenges.cloudflare.com/turnstile/v0/siteverify"
    timeout_ms: 2000

5.15 Runtime Override Priority (Important)

The following items can be changed dynamically in admin settings and have higher priority than config.yml:

  • SMTP (email) settings
  • Captcha settings
  • Telegram login settings
  • Order payment timeout minutes (payment_expire_minutes)

If you changed config.yml but behavior did not change, check admin settings first.

6. Environment variable mapping example

  • SERVER_MODE=release
  • DATABASE_DSN=host=127.0.0.1 ...
  • JWT_SECRET=...
  • USER_JWT_SECRET=...
  • DJ_DEFAULT_ADMIN_USERNAME=admin
  • DJ_DEFAULT_ADMIN_PASSWORD=<your-strong-password>
  • REDIS_HOST=127.0.0.1
  • CAPTCHA_TURNSTILE_SITE_KEY=...
  • TELEGRAM_AUTH_ENABLED=true

Rule: '.' in the configuration key is converted to '_'.

7. Common faults and troubleshooting

  • database is locked
    • Common in SQLite multi-concurrent writes
    • Check if 'max_open_conns' is '1' and confirm that the DSN is set to '_busy_timeout'
  • pq: sorry, too many clients already
    • PostgreSQL connections run out
    • Lowering 'max_open_conns', or raising the database 'max_connections'
  • Time display is scrambled (order time does not coincide with log time)
    • Check the 'TimeZone' of PostgreSQL DSN with the system time zone
  • Redis/queue is available but the message is not sent
    • Check 'queue.enabled', Redis connectivity, worker started
  • Orders stay in "pending payment" and never expire automatically
    • Check whether you started with -mode api only, or queue/Redis is unavailable so timeout tasks are not consumed

8. Pre-deployment checklist

  • [ ] server.mode=release
  • [ ] 'jwt.secret' and 'user_jwt.secret' have been replaced with high-strength random values
  • [ ] Database driver and DSN configuration compliance environment (SQLite/PostgreSQL)
  • [ ] The connection pool parameters match the database specifications
  • [ ] Redis/queue available (if enabled)
  • [ ] If using default startup mode all, ensure queue.enabled=true and queue Redis is reachable
  • [ ] If queue is intentionally disabled, start with -mode api and accept reduced async capabilities
  • [ ] CORS is restricted to real business domains
  • [ ] Email configuration has been authenticated (if enabled)

Released under the MIT License.