# Encryption at rest > CREATE DATABASE x WITH (encryption = on) - every file, log record and backup of it is encrypted; without the master key a copied disk shows nothing. - Category: Users & security - Keywords: encryption, encrypted, encryption at rest, at rest, encrypt, AES, AES-256, AES-256-GCM, GCM, ChaCha20, ChaCha20-Poly1305, master key, data key, key file, KDB\_MASTER\_KEY, KDB\_MASTER\_KEY\_FILE, KDB\_MASTER\_KEY\_PREVIOUS, KDB\_MASTER\_KEY\_PREVIOUS\_FILE, \_encryption.key, kdb key, key rotation, rotate key, WITH (encryption , TDE, stolen disk, snapshot - See also: [Databases and the data folder](databases.md), [TLS (https and sslmode=require)](tls.md), [Keeping kdb serve safe](server-security.md), [Backup, check, repair and restore](backup-and-safety.md), [kdb info, check, repair, dump-file](cli-maintenance.md) An **encrypted database** keeps every file on disk encrypted: the tables, the log, the manifests and its backups. Someone who copies the disk, a VPS snapshot or a backup sees only random bytes. Only a KDB server that has the **master key** can read it. It is **off by default**. You choose it for each database when you create it. | | Plain database | Encrypted database | |---|---|---| | Files on disk | readable with a hex editor | random bytes | | Backups | readable | encrypted too, and useless without the master key | | Needs | nothing | the master key when the server starts | | Queries | - | the same SQL, the same results | | Speed | - | see [speed](#speed) | Not covered: someone logged in to the running server as the `kdb` system user or as root can read the key and the memory. [Users and roles](users-and-roles.md) and [TLS](tls.md) protect the running server; encryption at rest protects the disk. ## Turn it on 1. Make a master key once, and keep it **outside** the data folder: ```bash kdb key new > /etc/kdb/master.key # 64 hex characters chmod 600 /etc/kdb/master.key ``` 2. Give it to the server, in one of these ways: Syntax: ```sql KDB_MASTER_KEY=<64 hex characters, or base64 of 32 bytes> KDB_MASTER_KEY_FILE= ``` 3. Create the database encrypted: ```sql CREATE DATABASE payroll WITH (encryption = on); USE payroll; CREATE TABLE salaries (name STRING, amount DECIMAL(2)); ``` - **Returns:** `database 'payroll' created encrypted`. - `WITH (encryption = on, pg_compat = on)` gives both options together ([CREATE DATABASE](databases.md#create-database)). - Without a master key, `CREATE DATABASE ... WITH (encryption = on)` fails with `encryption = on needs a master key` and makes nothing. - A database is encrypted or plain for its whole life. `ALTER DATABASE` cannot switch it, and `CREATE DATABASE IF NOT EXISTS ... WITH (encryption = on)` leaves a plain database that exists plain. To encrypt data you have, create an encrypted database and copy the tables into it (`INSERT INTO new_db.t SELECT * FROM old_db.t`). - `GET /api/databases` shows `"encrypted": true` for each encrypted database. In Docker, pass the key file as a secret and point `KDB_MASTER_KEY_FILE` at it, for example `-v /etc/kdb/master.key:/run/secrets/kdb_master_key:ro -e KDB_MASTER_KEY_FILE=/run/secrets/kdb_master_key`. Never put it in the data volume. ## Keys There are two keys, so that changing the master key never rewrites your data: | Key | What it is | Where it is | |---|---|---| | master key | 256 bits you give the server | `KDB_MASTER_KEY` or `KDB_MASTER_KEY_FILE`, never in the data folder | | data key | 256 random bits, one per database | `/_encryption.key`, **wrapped** (encrypted) by the master key | - The server refuses to start when `KDB_MASTER_KEY_FILE` is inside the data folder: a copy of the folder would hold the key too. - An encrypted database opened without the master key, or with another one, is refused with a clear message (`... is encrypted: give the server its master key ...`). It is never reported as damage, and nothing is changed. - **Keep the master key safe and keep a copy.** Without it the data cannot be read by anyone, KDB included. ## Change the master key Do this when a key may have leaked, or someone who knew it left: 1. `kdb key new` for the new key. 2. Start the server with the **new** key in `KDB_MASTER_KEY` and the **old** one in `KDB_MASTER_KEY_PREVIOUS` (or the `..._FILE` forms). 3. Each encrypted database re-wraps its data key with the new master key when it opens. It takes milliseconds, and no data file is rewritten. The server log says `the data key is now wrapped by the new master key`. 4. Re-wrap the backups too: `kdb key rotate ` (same variables). 5. Remove `KDB_MASTER_KEY_PREVIOUS`. ```bash KDB_MASTER_KEY_FILE=/etc/kdb/new.key KDB_MASTER_KEY_PREVIOUS_FILE=/etc/kdb/old.key \ kdb key rotate /data/_backups/payroll ``` Backups you do not re-wrap still need the old key. Keep old keys as long as backups that need them exist. ## Backups and restore - A backup of an encrypted database is encrypted too. Its files are copied as the encrypted bytes they are, and it takes the wrapped data key along (`_encryption.key`). - `kdb verify-backup`, `kdb restore` and `POST /api/restore` need the master key. The restored database is encrypted with the same data key. - A copy made off the machine (`--backup-copy`) is encrypted too. ## What is encrypted | Part | How | |---|---| | table files, catalog, manifests, backup manifests, temporary GROUP BY files | the whole file, in 64 KB pieces, each with AES-256-GCM and its own check | | the log (`wal/`, `wal_archive/`) | every record body on its own. Record headers (length, number, checksum) and commit times stay plain, so recovery after a crash works exactly as before | | `kdb.lock`, `_encryption.key` | not encrypted (the key file holds only the wrapped key) | | memory | not encrypted (see above) | - Every file gets a key of its own, made from the data key and a random value in the file's header. Every piece has a fresh random nonce. - Any changed byte, a piece moved inside a file, or a piece taken from another file fails its check and is reported as damage. A plain file put into an encrypted database is refused, never read as data. - The cipher is **AES-256-GCM** where the CPU has AES instructions (every x86-64 CPU since about 2010). Without them, new files use **ChaCha20-Poly1305**, which is fast in plain code. Each file says which one it uses, so a database moved between computers still opens. ## Speed Measured on the development PC (Windows, x86-64 with AES instructions), a table of 1 million rows in 12 day partitions, a GROUP BY over all of it: | Work | Plain | Encrypted | |---|---|---| | AES-256-GCM on one core | - | about 3-3.9 GB/s | | scan of loaded data (in memory) | 292 ms | 302 ms (about +3%, within noise) | | first scan after opening (reads and decrypts the files) | 749 ms | 883 ms (about +18%) | | size of the data files | 60.2 MB | 61.6 MB (+2.3%) | - **The first read after opening misses the goal.** The goal was at most 10% slower than a plain database; the first scan after opening was about 18% slower in this measurement. Work to close that gap is still open. - Queries on data already in memory do not decrypt anything: they cost the same. - Reading files, writing files (checkpoints, loads) and the log cost a little more CPU. A disk is slower than the cipher. - Without AES instructions (old or small CPUs) ChaCha20-Poly1305 runs at about 550 MB/s on one core.