Skip to main content

Database Configuration

SQLite and PostgreSQL are supported. This page covers setup and configuration for both.

SQLite

SQLite works well for most deployments, even with millions of resources.

Basic Setup

./mahresources -db-type=SQLITE -db-dsn=./mahresources.db -file-save-path=./files

Or with environment variables:

DB_TYPE=SQLITE
DB_DSN=./mahresources.db

Build Requirements

Building from source requires build tags for full SQLite functionality:

go build --tags 'json1 fts5'
TagPurpose
json1Enables JSON query support for metadata fields
fts5Enables Full-Text Search for notes and resources
warning

Pre-built binaries include these tags. Only relevant when building from source.

In-Memory Database

For testing or ephemeral usage:

./mahresources -memory-db -file-save-path=./files

Or use the combined ephemeral flag:

./mahresources -ephemeral

-memory-db and -ephemeral use a temporary SQLite database file with WAL mode for better concurrency under test load. Treat it as transient scratch storage rather than durable persistence.

Seeding from Existing Database

Start with a copy of an existing database (useful for testing or demos):

./mahresources -memory-db -seed-db=./production.db -file-save-path=./files

Changes are made to the in-memory copy and lost when the server stops.

-seed-db requires -memory-db or -ephemeral, and is SQLite-only. Combining it with -db-type=POSTGRES, or naming a file that does not exist, fails at startup.

Connection Pool Limits

For concurrent access scenarios (like parallel E2E tests), limit connections to reduce lock contention:

./mahresources -db-type=SQLITE -db-dsn=./test.db -max-db-connections=2

PostgreSQL

PostgreSQL is recommended for multi-user deployments or when you need advanced database features.

Basic Setup

./mahresources \
-db-type=POSTGRES \
-db-dsn="host=localhost port=5432 user=mahresources password=secret dbname=mahresources sslmode=disable" \
-file-save-path=./files

The DSN follows standard PostgreSQL connection string format.

With SSL

DB_TYPE=POSTGRES
DB_DSN="host=db.example.com port=5432 user=app password=secret dbname=mahresources sslmode=require"

Read-Only Connection for Saved Queries

Point saved SQL Queries at a replica or a read-only role so their arbitrary SQL never runs on the primary:

./mahresources \
-db-type=POSTGRES \
-db-dsn="host=primary.db user=app password=secret dbname=mahresources" \
-db-readonly-dsn="host=replica.db user=app password=secret dbname=mahresources"

This connection is used by exactly one code path: executing a saved SQL Query at /query. Ordinary reads (lists, single-item fetches, search, MRQL) all go through the -db-dsn connection, so this setting offloads no general read traffic.

Database Logging

Control database query logging with -db-log-file:

ValueBehavior
STDOUTLog queries to standard output
(empty)Disable query logging
/path/to/fileLog queries to specified file
# Log to stdout (useful for debugging)
./mahresources -db-type=SQLITE -db-dsn=./test.db -db-log-file=STDOUT

# Log to file
./mahresources -db-type=SQLITE -db-dsn=./test.db -db-log-file=/var/log/mahresources-db.log

Slow Query Logging

Set -db-slow-query-threshold to record only queries that exceed a duration, so they can be reviewed and optimized later:

./mahresources -db-type=SQLITE -db-dsn=./test.db -db-slow-query-threshold=200ms

Slow queries are written to two places:

  • The DB log. With -db-log-file unset, slow queries (and query errors) go to STDOUT on their own. With -db-log-file set, the full query log is kept and slow queries are additionally tagged with a SLOW SQL >= prefix.
  • The application log. Each slow query is stored as a warning log entry with entity type sql, browsable at /logs (filter by level warning) and via GET /v1/logs. The entry's details contain the SQL text, duration in milliseconds, row count, and the code location that issued the query. Entries are subject to the usual -cleanup-logs-days retention.

Application-log writes are asynchronous and never block queries; under sustained load, excess entries are dropped rather than queued. Statements against the log table itself are never recorded.

Startup Optimizations

On large databases with millions of resources, certain startup operations can be slow.

Skip Full-Text Search Initialization

./mahresources -skip-fts -db-type=SQLITE -db-dsn=./large.db -file-save-path=./files

Skips FTS index creation and update at startup. Search still works: it falls back to LIKE (ILIKE on PostgreSQL) matching, which is slower on large tables and has no relevance ranking or tokenization.

Skip Version Migration

./mahresources -skip-version-migration -db-type=SQLITE -db-dsn=./large.db -file-save-path=./files

Skips the resource version migration that runs at startup. Safe to use after the initial migration has completed.

Configuration Reference

FlagEnv VariableDescription
-db-typeDB_TYPEDatabase type: SQLITE or POSTGRES
-db-dsnDB_DSNDatabase connection string
-db-readonly-dsnDB_READONLY_DSNConnection that saved SQL Queries execute on
-db-log-fileDB_LOG_FILEQuery log destination
-db-slow-query-thresholdDB_SLOW_QUERY_THRESHOLDLog queries slower than this duration (e.g. 200ms) to the DB log and the application log; 0 disables
-memory-dbMEMORY_DB=1Use in-memory SQLite
-seed-dbSEED_DBSQLite file to seed memory-db
-max-db-connectionsMAX_DB_CONNECTIONSConnection pool size limit
-skip-ftsSKIP_FTS=1Skip FTS initialization
-skip-version-migrationSKIP_VERSION_MIGRATION=1Skip version migration