MHSanaei/3x-ui · critical

XUI_DB_TYPE=postgres but XUI_DB_DSN is empty

Error message

XUI_DB_TYPE=postgres but XUI_DB_DSN is empty

What it means

InitDB refuses to continue when config.GetDBKind() returns 'postgres' but the DSN environment variable XUI_DB_DSN is empty. PostgreSQL has no default DSN in this codebase, so there is nothing to connect to; failing fast prevents GORM from attempting a meaningless connection loop. This is a startup-time configuration validation error.

Source

Thrown at internal/database/db.go:1986

			log.New(os.Stdout, "\r\n", log.LstdFlags),
			logger.Config{
				SlowThreshold:             time.Second,
				LogLevel:                  logger.Info,
				IgnoreRecordNotFoundError: true,
				Colorful:                  true,
			},
		)
	} else {
		gormLogger = logger.Discard
	}
	c := &gorm.Config{Logger: gormLogger, DisableForeignKeyConstraintWhenMigrating: true}

	var err error
	switch config.GetDBKind() {
	case "postgres":
		dsn := config.GetDBDSN()
		if dsn == "" {
			return errors.New("XUI_DB_TYPE=postgres but XUI_DB_DSN is empty")
		}
		db, err = openPostgresWithRetry(dsn, c)
		if err != nil {
			return err
		}
	default:
		dir := path.Dir(dbPath)
		if err = os.MkdirAll(dir, 0o755); err != nil {
			return err
		}
		if err = cleanupSQLiteBackupDirs(filepath.Dir(dbPath)); err != nil {
			log.Printf("clean SQLite backup directories: %v", err)
		}

		sync := sqliteSynchronous()
		journal := sqliteJournalMode()
		dsn := dbPath + "?_journal_mode=" + journal + "&_busy_timeout=10000&_synchronous=" + sync + "&_txlock=immediate"
		db, err = gorm.Open(sqlite.Open(dsn), c)

View on GitHub (pinned to ad32144c42)

Solutions

  1. Set XUI_DB_DSN to a full libpq/pq URL, e.g. postgres://user:pass@host:5432/xui?sslmode=disable
  2. Verify with: XUI_DB_TYPE=postgres XUI_DB_DSN=... ./x-ui migrate-db or just restart the service
  3. If Postgres was not intended, unset XUI_DB_TYPE to fall back to SQLite at /etc/x-ui/x-ui.db

Example fix

# before
XUI_DB_TYPE=postgres

# after
XUI_DB_TYPE=postgres
XUI_DB_DSN=postgres://xui:secret@db.internal:5432/xui?sslmode=require
Defensive patterns

Strategy: validation

Validate before calling

if os.Getenv("XUI_DB_TYPE") == "postgres" && os.Getenv("XUI_DB_DSN") == "" {
    log.Fatal("XUI_DB_DSN is required when XUI_DB_TYPE=postgres")
}

Try / catch

if err := database.InitDB(dbPath); err != nil {
    if strings.Contains(err.Error(), "XUI_DB_DSN is empty") {
        log.Fatal("set XUI_DB_DSN, e.g. postgres://user:pass@host:5432/xui")
    }
    log.Fatal(err)
}

Prevention

When it happens

Trigger: Setting XUI_DB_TYPE=postgres without XUI_DB_DSN in /etc/default/x-ui (or the systemd unit env); typo'ing the DSN variable name; exporting XUI_DB_TYPE in a shell but forgetting the DSN when running ./x-ui or the migration subcommand.

Common situations: Migrating a panel from SQLite to Postgres per the documented flow; CI jobs that set only the DB type; docker deployments missing the -e XUI_DB_DSN flag.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/0478388ad9ffd6ec. Report an issue: GitHub.