Update docs, manual changes

This commit is contained in:
binwiederhier
2026-02-20 16:36:41 -05:00
parent 13a3062a7f
commit a28d8e7924
6 changed files with 57 additions and 48 deletions

View File

@@ -380,7 +380,7 @@ func createUserManager(c *cli.Context) (*user.Manager, error) {
} }
var store user.Store var store user.Store
if databaseURL != "" { if databaseURL != "" {
pool, dbErr := db.Open(databaseURL) pool, dbErr := db.OpenPostgres(databaseURL)
if dbErr != nil { if dbErr != nil {
return nil, dbErr return nil, dbErr
} }

View File

@@ -19,11 +19,11 @@ const (
defaultMaxOpenConns = 10 defaultMaxOpenConns = 10
) )
// Open opens a PostgreSQL database connection pool from a DSN string. It supports custom // OpenPostgres opens a PostgreSQL database connection pool from a DSN string. It supports custom
// query parameters for pool configuration: pool_max_conns (default 10), pool_max_idle_conns, // query parameters for pool configuration: pool_max_conns (default 10), pool_max_idle_conns,
// pool_conn_max_lifetime, and pool_conn_max_idle_time. These parameters are stripped from // pool_conn_max_lifetime, and pool_conn_max_idle_time. These parameters are stripped from
// the DSN before passing it to the driver. // the DSN before passing it to the driver.
func Open(dsn string) (*sql.DB, error) { func OpenPostgres(dsn string) (*sql.DB, error) {
u, err := url.Parse(dsn) u, err := url.Parse(dsn)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid database URL: %w", err) return nil, fmt.Errorf("invalid database URL: %w", err)

View File

@@ -30,7 +30,7 @@ func CreateTestSchema(t *testing.T) string {
q.Set("pool_max_conns", testPoolMaxConns) q.Set("pool_max_conns", testPoolMaxConns)
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
dsn = u.String() dsn = u.String()
setupDB, err := db.Open(dsn) setupDB, err := db.OpenPostgres(dsn)
require.Nil(t, err) require.Nil(t, err)
_, err = setupDB.Exec(fmt.Sprintf("CREATE SCHEMA %s", schema)) _, err = setupDB.Exec(fmt.Sprintf("CREATE SCHEMA %s", schema))
require.Nil(t, err) require.Nil(t, err)
@@ -39,7 +39,7 @@ func CreateTestSchema(t *testing.T) string {
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
schemaDSN := u.String() schemaDSN := u.String()
t.Cleanup(func() { t.Cleanup(func() {
cleanDB, err := db.Open(dsn) cleanDB, err := db.OpenPostgres(dsn)
if err == nil { if err == nil {
cleanDB.Exec(fmt.Sprintf("DROP SCHEMA %s CASCADE", schema)) cleanDB.Exec(fmt.Sprintf("DROP SCHEMA %s CASCADE", schema))
cleanDB.Close() cleanDB.Close()
@@ -54,7 +54,7 @@ func CreateTestSchema(t *testing.T) string {
func CreateTestDB(t *testing.T) *sql.DB { func CreateTestDB(t *testing.T) *sql.DB {
t.Helper() t.Helper()
schemaDSN := CreateTestSchema(t) schemaDSN := CreateTestSchema(t)
testDB, err := db.Open(schemaDSN) testDB, err := db.OpenPostgres(schemaDSN)
require.Nil(t, err) require.Nil(t, err)
t.Cleanup(func() { t.Cleanup(func() {
testDB.Close() testDB.Close()

View File

@@ -125,36 +125,29 @@ using Docker Compose (i.e. `docker-compose.yml`):
command: serve command: serve
``` ```
## Message cache ## Database options
If desired, ntfy can temporarily keep notifications in an in-memory or an on-disk cache. Caching messages for a short period ntfy uses a database for storing messages ([message cache](#message-cache)), users and [access control](#access-control), and [web push](#web-push) subscriptions.
of time is important to allow [phones](subscribe/phone.md) and other devices with brittle Internet connections to be able to retrieve You can choose between **SQLite** and **PostgreSQL** as the database backend.
notifications that they may have missed.
By default, ntfy keeps messages **in-memory for 12 hours**, which means that **cached messages do not survive an application ### SQLite
restart**. You can override this behavior using the following config settings: By default, ntfy uses SQLite with separate database files for each store. This is the simplest setup and requires
no external dependencies:
* `cache-file`: if set, ntfy will store messages in a SQLite based cache (default is empty, which means in-memory cache). * `cache-file`: Database file for the [message cache](#message-cache).
**This is required if you'd like messages to be retained across restarts**. * `auth-file`: Database file for authentication and [access control](#access-control). If set, enables auth.
* `cache-duration`: defines the duration for which messages are stored in the cache (default is `12h`). * `web-push-file`: Database file for [web push](#web-push) subscriptions.
You can also entirely disable the cache by setting `cache-duration` to `0`. When the cache is disabled, messages are only ### PostgreSQL
passed on to the connected subscribers, but never stored on disk or even kept in memory longer than is needed to forward As an alternative, you can configure ntfy to use PostgreSQL for **all** database-backed stores by setting the
the message to the subscribers. `database-url` option to a PostgreSQL connection string:
Subscribers can retrieve cached messaging using the [`poll=1` parameter](subscribe/api.md#poll-for-messages), as well as the
[`since=` parameter](subscribe/api.md#fetch-cached-messages).
## PostgreSQL database
By default, ntfy uses SQLite for all database-backed stores. As an alternative, you can configure ntfy to use PostgreSQL
by setting the `database-url` option to a PostgreSQL connection string:
```yaml ```yaml
database-url: "postgres://user:pass@host:5432/ntfy" database-url: "postgres://user:pass@host:5432/ntfy"
``` ```
When `database-url` is set, ntfy will use PostgreSQL for all database-backed stores (message cache, user manager, When `database-url` is set, ntfy will use PostgreSQL for the [message cache](#message-cache),
and web push subscriptions) instead of SQLite. The `cache-file`, `auth-file`, and `web-push-file` options must not [access control](#access-control), and [web push](#web-push) subscriptions instead of SQLite. The `cache-file`,
be set in this case. `auth-file`, and `web-push-file` options **must not** be set in this case.
Note that setting `database-url` implicitly enables authentication and access control (equivalent to setting Note that setting `database-url` implicitly enables authentication and access control (equivalent to setting
`auth-file` with SQLite). The default access is `read-write`, so anonymous users can still read and write to all `auth-file` with SQLite). The default access is `read-write`, so anonymous users can still read and write to all
@@ -162,11 +155,10 @@ topics. To restrict access, set `auth-default-access` to `deny-all` (see [access
You can also set this via the environment variable `NTFY_DATABASE_URL` or the command line flag `--database-url`. You can also set this via the environment variable `NTFY_DATABASE_URL` or the command line flag `--database-url`.
### Connection pool settings You can tune the PostgreSQL connection pool by appending query parameters to the database URL:
You can tune the connection pool by appending query parameters to the database URL:
| Parameter | Default | Description | | Parameter | Default | Description |
|---|---|---| |---------------------------|---------|----------------------------------------------------------------------------------|
| `pool_max_conns` | 10 | Maximum number of open connections to the database | | `pool_max_conns` | 10 | Maximum number of open connections to the database |
| `pool_max_idle_conns` | - | Maximum number of idle connections in the pool | | `pool_max_idle_conns` | - | Maximum number of idle connections in the pool |
| `pool_conn_max_lifetime` | - | Maximum amount of time a connection may be reused (Go duration, e.g. `5m`, `1h`) | | `pool_conn_max_lifetime` | - | Maximum amount of time a connection may be reused (Go duration, e.g. `5m`, `1h`) |
@@ -178,6 +170,23 @@ Example:
database-url: "postgres://user:pass@host:5432/ntfy?pool_max_conns=50&pool_conn_max_idle_time=5m" database-url: "postgres://user:pass@host:5432/ntfy?pool_max_conns=50&pool_conn_max_idle_time=5m"
``` ```
## Message cache
If desired, ntfy can temporarily keep notifications in an in-memory or an on-disk cache. Caching messages for a short period
of time is important to allow [phones](subscribe/phone.md) and other devices with brittle Internet connections to be able to retrieve
notifications that they may have missed.
By default, ntfy keeps messages **in-memory for 12 hours**, which means that **cached messages do not survive an application
restart**. You can override this behavior by setting `cache-file` (SQLite) or `database-url` (PostgreSQL).
* `cache-duration`: defines the duration for which messages are stored in the cache (default is `12h`).
You can also entirely disable the cache by setting `cache-duration` to `0`. When the cache is disabled, messages are only
passed on to the connected subscribers, but never stored on disk or even kept in memory longer than is needed to forward
the message to the subscribers.
Subscribers can retrieve cached messaging using the [`poll=1` parameter](subscribe/api.md#poll-for-messages), as well as the
[`since=` parameter](subscribe/api.md#fetch-cached-messages).
## Attachments ## Attachments
If desired, you may allow users to upload and [attach files to notifications](publish.md#attachments). To enable If desired, you may allow users to upload and [attach files to notifications](publish.md#attachments). To enable
this feature, you have to simply configure an attachment cache directory and a base URL (`attachment-cache-dir`, `base-url`). this feature, you have to simply configure an attachment cache directory and a base URL (`attachment-cache-dir`, `base-url`).
@@ -227,7 +236,7 @@ To set up auth, **configure the following options**:
* `auth-file` is the user/access database (SQLite); it is created automatically if it doesn't already exist; suggested * `auth-file` is the user/access database (SQLite); it is created automatically if it doesn't already exist; suggested
location `/var/lib/ntfy/user.db` (easiest if deb/rpm package is used). Alternatively, if `database-url` is set, location `/var/lib/ntfy/user.db` (easiest if deb/rpm package is used). Alternatively, if `database-url` is set,
auth is automatically enabled using PostgreSQL (see [PostgreSQL database](#postgresql-database)). auth is automatically enabled using PostgreSQL (see [database options](#database-options)).
* `auth-default-access` defines the default/fallback access if no access control entry is found; it can be * `auth-default-access` defines the default/fallback access if no access control entry is found; it can be
set to `read-write` (default), `read-only`, `write-only` or `deny-all`. **If you are setting up a private instance, set to `read-write` (default), `read-only`, `write-only` or `deny-all`. **If you are setting up a private instance,
you'll want to set this to `deny-all`** (see [private instance example](#example-private-instance)). you'll want to set this to `deny-all`** (see [private instance example](#example-private-instance)).
@@ -1794,7 +1803,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
| `key-file` | `NTFY_KEY_FILE` | *filename* | - | HTTPS/TLS private key file, only used if `listen-https` is set. | | `key-file` | `NTFY_KEY_FILE` | *filename* | - | HTTPS/TLS private key file, only used if `listen-https` is set. |
| `cert-file` | `NTFY_CERT_FILE` | *filename* | - | HTTPS/TLS certificate file, only used if `listen-https` is set. | | `cert-file` | `NTFY_CERT_FILE` | *filename* | - | HTTPS/TLS certificate file, only used if `listen-https` is set. |
| `firebase-key-file` | `NTFY_FIREBASE_KEY_FILE` | *filename* | - | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. See [Firebase (FCM)](#firebase-fcm). | | `firebase-key-file` | `NTFY_FIREBASE_KEY_FILE` | *filename* | - | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. See [Firebase (FCM)](#firebase-fcm). |
| `database-url` | `NTFY_DATABASE_URL` | *string (connection URL)* | - | PostgreSQL connection string (e.g. `postgres://user:pass@host:5432/ntfy`). If set, uses PostgreSQL for all database-backed stores (message cache, user manager, web push) instead of SQLite. See [PostgreSQL database](#postgresql-database). | | `database-url` | `NTFY_DATABASE_URL` | *string (connection URL)* | - | PostgreSQL connection string (e.g. `postgres://user:pass@host:5432/ntfy`). If set, uses PostgreSQL for all database-backed stores (message cache, user manager, web push) instead of SQLite. See [database options](#database-options). |
| `cache-file` | `NTFY_CACHE_FILE` | *filename* | - | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. See [message cache](#message-cache). | | `cache-file` | `NTFY_CACHE_FILE` | *filename* | - | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. See [message cache](#message-cache). |
| `cache-duration` | `NTFY_CACHE_DURATION` | *duration* | 12h | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. Set this to `0` to disable the cache entirely. | | `cache-duration` | `NTFY_CACHE_DURATION` | *duration* | 12h | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. Set this to `0` to disable the cache entirely. |
| `cache-startup-queries` | `NTFY_CACHE_STARTUP_QUERIES` | *string (SQL queries)* | - | SQL queries to run during database startup; this is useful for tuning and [enabling WAL mode](#message-cache) | | `cache-startup-queries` | `NTFY_CACHE_STARTUP_QUERIES` | *string (SQL queries)* | - | SQL queries to run during database startup; this is useful for tuning and [enabling WAL mode](#message-cache) |

View File

@@ -178,11 +178,11 @@ func New(conf *Config) (*Server, error) {
if payments.Available && conf.StripeSecretKey != "" { if payments.Available && conf.StripeSecretKey != "" {
stripe = newStripeAPI() stripe = newStripeAPI()
} }
// Open shared PostgreSQL connection pool if configured // OpenPostgres shared PostgreSQL connection pool if configured
var pool *sql.DB var pool *sql.DB
if conf.DatabaseURL != "" { if conf.DatabaseURL != "" {
var err error var err error
pool, err = db.Open(conf.DatabaseURL) pool, err = db.OpenPostgres(conf.DatabaseURL)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -35,7 +35,7 @@ func forEachBackend(t *testing.T, f func(t *testing.T, newStore newStoreFunc)) {
t.Run("postgres", func(t *testing.T) { t.Run("postgres", func(t *testing.T) {
schemaDSN := dbtest.CreateTestSchema(t) schemaDSN := dbtest.CreateTestSchema(t)
f(t, func() Store { f(t, func() Store {
pool, err := db.Open(schemaDSN) pool, err := db.OpenPostgres(schemaDSN)
require.Nil(t, err) require.Nil(t, err)
store, err := NewPostgresStore(pool) store, err := NewPostgresStore(pool)
require.Nil(t, err) require.Nil(t, err)