etherpad-lite/doc/database.md
John McLear 01d0b08a4e
docs: migrate useful wiki content into the manual (#7990) (#7994)
* docs: migrate useful wiki content into the VitePress manual (#7990)

The GitHub wiki is being retired; documentation should ship with the
software. This migrates the still-accurate, non-duplicate wiki pages into
the published VitePress site (doc/**/*.md + the sidebar in
doc/.vitepress/config.mts) so they are versioned, searchable and portable:

- deployment.md: reverse-proxy configs (Nginx/Apache/Caddy/Traefik/
  HAProxy) with the WebSocket-upgrade rules, subdirectory hosting via
  X-Proxy-Path, native HTTPS via the ssl block, a systemd unit, and the
  Istio manifest (with the Redis-adapter multi-replica caveat).
- accessibility.md: editor keyboard shortcuts (verified against
  ace2_inner.ts / broadcast_slider.ts / pad_editbar.ts), toolbar
  navigation, NVDA notes.
- faq.md: install methods, URL-path reference, listing/deleting pads
  (API-first), backup/restore, and history pruning.
- development.md: source-tree tour, the pad<->format conversion pipeline,
  the internal DB API, and the Fontello toolbar-icon workflow.
- database.md: the key/value schema plus connecting MySQL/PostgreSQL/Redis
  backends and a pgloader MySQL->PostgreSQL migration (database docs were
  previously absent from the VitePress site).

Every page was checked against the current source before inclusion:
corrected the apt instructions to the live signed repo (stable/main,
signed-by key), dropped the unpublished snap, fixed the Redis dbSettings
(flat host/port/password or url, not the obsolete client_options),
dropped charset from the PostgreSQL example, and removed a phantom
getEtherpad API reference. The VitePress site builds cleanly
(pnpm run docs:build) with the dead-link checker enabled.

Closes #7990

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: add verified hands-on changeset/atext walkthrough (#7990)

Migrate the practical Changeset-library tutorial from the wiki into
changeset_library.md, rewritten against the current API: unpack(),
deserializeOps() (replacing the deprecated opIterator) and
new AttributePool() (replacing the removed AttributePoolFactory). Every
example output was produced by running the code against the current
Changeset.ts / AttributePool.ts, not copied from the wiki. Also fixes a
stale ether/etherpad-lite source link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:52:33 +01:00

5.8 KiB

Database structure

Keys and their values

groups

A list of all existing groups (a JSON object with groupIDs as keys and 1 as values).

pad:$PADID

Contains all information about pads

  • atext - the latest attributed text
  • pool - the attribute pool
  • head - the number of the latest revision
  • chatHead - the number of the latest chat entry
  • public - flag that disables security for this pad
  • passwordHash - string that contains a salted sha512 sum of this pad's password

pad:$PADID:revs:$REVNUM

Saves a revision $REVNUM of pad $PADID

  • meta
    • author - the autorID of this revision
    • timestamp - the timestamp of when this revision was created
  • changeset - the changeset of this revision

pad:$PADID:chat:$CHATNUM

Saves a chat entry with num $CHATNUM of pad $PADID

  • text - the text of this chat entry
  • userId - the authorID of this chat entry
  • time - the timestamp of this chat entry

pad2readonly:$PADID

Translates a padID to a readonlyID

readonly2pad:$READONLYID

Translates a readonlyID to a padID

token2author:$TOKENID

Translates a token to an authorID

globalAuthor:$AUTHORID

Information about an author

  • name - the name of this author as shown in the pad
  • colorID - the colorID of this author as shown in the pad

mapper2group:$MAPPER

Maps an external application identifier to an internal group

mapper2author:$MAPPER

Maps an external application identifier to an internal author

group:$GROUPID

a group of pads

  • pads - object with pad names in it, values are 1

session:$SESSIONID

a session between an author and a group

  • groupID - the groupID the session belongs too
  • authorID - the authorID the session belongs too
  • validUntil - the timestamp until this session is valid

author2sessions:$AUTHORID

saves the sessions of an author

  • sessionsIDs - object with sessionIDs in it, values are 1

group2sessions:$GROUPID

  • sessionsIDs - object with sessionIDs in it, values are 1

Connecting to a database backend

Etherpad stores everything in a single key/value table through ueberDB, so the same data model works across many backends. The backend is selected with dbType in settings.json, and backend-specific connection options go in dbSettings.

The default dirty backend writes to a local file (var/dirty.db) and needs no setup, which is convenient for development but not recommended for production. For a production instance, point Etherpad at a real database such as MySQL/MariaDB, PostgreSQL or Redis. Etherpad creates its own table on first run; you only need to provision an empty database and a user with access to it.

MySQL / MariaDB

Create the database and a user, then grant access:

CREATE DATABASE `etherpad` CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER 'etherpad'@'localhost' IDENTIFIED BY 'a-secure-password';
GRANT CREATE,ALTER,SELECT,INSERT,UPDATE,DELETE ON `etherpad`.* TO 'etherpad'@'localhost';

Then configure settings.json:

"dbType": "mysql",
"dbSettings": {
  "user":     "etherpad",
  "host":     "localhost",
  "port":     3306,
  "password": "a-secure-password",
  "database": "etherpad",
  "charset":  "utf8mb4"
}

Setting charset to utf8mb4 is strongly recommended so that the full range of Unicode (including emoji) is stored correctly. To connect over a local socket instead of TCP, replace host/port with "socketPath": "/var/run/mysqld/mysqld.sock".

PostgreSQL

Create the user and a database owned by it:

CREATE USER etherpad WITH PASSWORD 'a-secure-password';
CREATE DATABASE etherpad OWNER etherpad;

Then configure settings.json:

"dbType": "postgres",
"dbSettings": {
  "user":     "etherpad",
  "host":     "localhost",
  "port":     5432,
  "password": "a-secure-password",
  "database": "etherpad"
}

The dbSettings object is passed straight to the node-postgres connection pool, so any option it accepts (including a single "connectionString") works. On Debian/Ubuntu you can use peer authentication over the local socket by setting "host": "/var/run/postgresql" and an empty password, provided the operating-system user that runs Etherpad matches the PostgreSQL role.

Redis

Install Redis and make sure it persists data to disk. Configure settings.json with either discrete fields or a single connection URL:

"dbType": "redis",
"dbSettings": {
  "host":     "localhost",
  "port":     6379,
  "password": "a-secure-redis-password"
}
"dbType": "redis",
"dbSettings": {
  "url": "redis://:a-secure-redis-password@localhost:6379"
}

Migrating from MySQL to PostgreSQL

pgloader can copy an existing Etherpad database from MySQL to PostgreSQL. Stop Etherpad first so the source database is quiescent.

sudo apt-get install postgresql pgloader

# Create the target role and database
sudo -u postgres createuser etherpad
sudo -u postgres createdb -O etherpad etherpad

# Describe and run the migration
cat > pgloader.load <<'EOF'
LOAD DATABASE
    FROM     mysql://etherpad:MYSQL_PASSWORD@127.0.0.1/etherpad
    INTO     postgresql:///etherpad
WITH        preserve index names, prefetch rows = 100
ALTER SCHEMA 'etherpad' RENAME TO 'public';
EOF

pgloader --verbose pgloader.load

Afterwards set the PostgreSQL user's password and make sure it can read and write the migrated table:

ALTER USER etherpad WITH PASSWORD 'a-secure-password';
GRANT pg_read_all_data  TO etherpad;
GRANT pg_write_all_data TO etherpad;

Then point settings.json at PostgreSQL as shown above and start Etherpad.

::: tip To move data between any two backends supported by ueberDB, you can also use the migrateDB CLI tool, which reads every record from a source database descriptor and writes it to a target one. See the CLI chapter. :::