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.port ⇢ SERVER_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.