Database
The panel works with PostgreSQL, MySQL or MariaDB, and SQLite. The choice is set with two
variables in config.env:
DATABASE_DRIVER=postgres
DATABASE_URL=postgres://gameap:password@localhost:5432/gameap?sslmode=disable
Which One to Choose
| DBMS | When it fits |
|---|---|
| SQLite | A single panel, up to a few dozen game servers. No separate service needed |
| PostgreSQL | When high performance is needed. Recommended for running multiple panel instances |
| MySQL, MariaDB | The familiar option, including when upgrading from GameAP 3. Slightly slower than PostgreSQL, but still suitable for multiple instances |
For a typical single-server installation SQLite is enough: it requires no separate service, no setup, and no backup more complex than copying a file.
Connection String
PostgreSQL
DATABASE_DRIVER=postgres
DATABASE_URL=postgres://user:password@host:5432/database?sslmode=disable
The driver names are interchangeable: postgres, postgresql, pgsql, pg, pgx.
The sslmode parameter sets the connection encryption mode: disable for a local database,
require or verify-full for a remote one.
MySQL and MariaDB
DATABASE_DRIVER=mysql
DATABASE_URL=user:password@tcp(host:3306)/database?parseTime=true
The format differs from the usual URL with a scheme — it is the Go driver format. The
parseTime=true parameter is required.
Connecting through a socket:
DATABASE_URL=user:password@unix(/var/run/mysqld/mysqld.sock)/database?parseTime=true
SQLite
DATABASE_DRIVER=sqlite
DATABASE_URL=file:/var/lib/gameap/db.sqlite?_busy_timeout=5000&_journal_mode=WAL&cache=shared
The file is created automatically. The parameters in the example enable WAL journaling and lock waiting — keep them, they noticeably improve behavior under concurrent requests.
The directory with the database file must be writable by the user the panel runs as.
inmemory
DATABASE_DRIVER=inmemory
DATABASE_URL=inmemory
Data is kept in RAM only and is lost on restart. Meant for tests; not suitable for a production installation.
DATABASE_URL must be set even here: the panel checks that it is non-empty before it looks at
the driver and will not start without it. The value itself is not used.
Migrations
The panel applies migrations itself at startup — there is no separate command. The schema version is stored in a service table inside the same database.
Two practical rules follow from this:
- A panel upgrade changes the schema on the very first start. Make the backup before it.
- Rolling back to a previous panel version without restoring the database will not work —
the schema has already changed. Some migrations cannot be undone even in principle: migration
022 (4.5.0) lowercases every login and email, and case folding is irreversible — its rollback
step does nothing; migrations 016 and 023 (4.5.0) delete duplicate rows from
plugin_storageandserver_user. A database dump is the only way back.
Migrations are applied even with gaps in the numbering, so skipping an intermediate panel
version during an upgrade is safe for the schema. It is not safe for config.env: when a
variable is renamed, the panel keeps reading the old name for exactly one release and then
drops it, so an operator who skips a release silently loses that setting.
gameapctl panel upgrade rewrites config.env on every upgrade and remembers the whole chain
of renames; if you upgrade by other means, check config.env against the
config.env Reference afterwards.
Some migrations rewrite tables rather than just add them. Upgrading from 4.4.1 or earlier
applies 014_widen_port_columns, which on PostgreSQL runs ALTER TABLE … TYPE INTEGER on the
port columns of dedicated_servers and servers (they were SMALLINT, so ports above 32767
did not fit). PostgreSQL rewrites those tables and holds an exclusive lock while it does, so
the first start after the upgrade can take noticeably longer on a large installation. On
MySQL/MariaDB and SQLite the same migration is a no-op.
4.5.0 also adds a unique index on server_user (user_id, server_id) (migration 023) after
removing duplicates. A full dump made with the commands below restores the old schema together
with the migration-version table, so the panel simply re-applies the migration on the next
start. Only a data-only restore (--no-create-info, copying selected tables) into an already
migrated 4.5 database can fail on duplicate pairs — deduplicate them first or restore the full
dump.
Backup
The panel does not make backups — neither by itself nor via gameapctl. Set up backups with
the DBMS tools.
Restore a full dump into an empty database — create it fresh, or drop and recreate the existing one, before running the commands below. A dump does not clear what is already in the target, so loading it over an existing schema fails on the tables that are already there.
PostgreSQL
pg_dump -U gameap gameap > gameap-$(date +%F).sql
Restoring:
psql -U gameap gameap < gameap-2026-08-02.sql
MySQL and MariaDB
mysqldump -u gameap -p gameap > gameap-$(date +%F).sql
Restoring:
mysql -u gameap -p gameap < gameap-2026-08-02.sql
SQLite
Stopping the panel is not necessary if you use the built-in command:
sqlite3 /var/lib/gameap/db.sqlite ".backup '/backup/gameap-$(date +%F).sqlite'"
Simply copying the file while the panel is running can produce a corrupted copy because of the WAL journal.
What Else to Save
The database is not enough. Along with it, save:
config.env— it holdsAUTH_SECRETandENCRYPTION_KEY. Restore the same values that were in place when the backup was made: with a differentAUTH_SECRET, issued tokens stop being accepted. IfENCRYPTION_KEYwas set on the installation, encrypted data cannot be restored without it and two-factor authentication stops working for every user; since 4.5 this also covers plugin secrets — theplugin_secretstable is encrypted with this key (AES-256-GCM) and is unrecoverable without it. IfENCRYPTION_KEYwas never set, the TOTP secrets are encrypted with a key derived fromAUTH_SECRET— then savingAUTH_SECRETis enough, andENCRYPTION_KEYmust not be added during the restore: it breaks 2FA for every user;- the panel files directory — it holds the gRPC certificates daemons connect with, the ACME
data and the files of installed plugins. The path is set by
FILES_LOCAL_BASE_PATH.
Game server files live on the dedicated servers and are not part of the panel backup.
Switching to Another DBMS
There is no built-in data transfer between DBMSes: the PostgreSQL, MySQL, and SQLite schemas are created independently, and the panel provides no migration tool.
The procedure:
- Back up the current database.
- Prepare the new database and point
DATABASE_DRIVERandDATABASE_URLat it. - Start the panel — it will create the schema from scratch.
- Migrate the data: recreate the users, dedicated servers, games, and game servers manually or via the API.
Game servers themselves are untouched: their files stay on the dedicated servers; it is enough to describe the servers in the panel with the same directories and ports.
Loading a dump from one DBMS directly into another will not work — column types and syntax differ.
Multiple Panel Instances
For a multi-instance installation, use PostgreSQL or MySQL. SQLite will not do: it is not designed for several clients working with the database file at the same time.
Besides the database, you will need a shared cache, shared event exchange, and shared file storage. See Multiple Panel Instances for details.
Checking
If the panel fails to start because of the database, the log will contain a connection or migration message:
journalctl -u gameap -n 50 --no-pager
Common causes: a wrong DATABASE_URL format (especially for MySQL — it needs the Go driver
format, not a URL with a scheme), no permissions on the SQLite file directory, the database not
created, or the user not granted access to it.