Install
Getting an instance running: on your own machine, in production with systemd, or in Docker. For building the binaries, see Building from source.
Local development
To code against a local instance, run the published image:
docker run -d --rm --name tamarackdb -p 127.0.0.1:8085:8085 \
-e TAMARACKDB_DEV_MODE=true \
-e TAMARACKDB_LOG_LEVEL=debug \
ghcr.io/tamarackdb/tamarackdb:latest- The API is at
http://127.0.0.1:8085, reachable from your machine only. TAMARACKDB_DEV_MODE=trueturns onPOST /reset, to start each test run from an empty store (see Development mode).TAMARACKDB_LOG_LEVEL=debuglogs every request, which helps while you write the integration (see Logs). Read them withdocker logs -f tamarackdb.- The data lives in the container’s own volume, and
--rmdeletes it when the container stops (docker stop tamarackdb). Mount a named volume on/datato keep it across runs (see Docker).
Authentication stays off: the port only listens on your own machine.
Production
Check each point before an instance holds real data:
- A dedicated user. Run the server as its own system user, never as root, and run every command that touches
dataDiras that user (see Run). - A private data directory.
dataDiris readable by the server’s user only (0700).tamarackdb-initcreates it that way; give a directory you create yourself the same permissions (see Security). - The same host, over the unix socket. Installed directly on the host, run TamarackDB next to the application, on
the socket. If the application runs as another user, set
socketMode = "0660"and add that user to the server’s group (see Security). In Docker, use TCP on a private network instead (see Alongside the application). - A reverse proxy and a token over the network. If the application must reach TamarackDB from another host, put a
reverse proxy in front of the socket to handle TLS, and turn
enableAuthon (see Security). - A protected configuration file. A
config.tomlthat holdsauthTokenis readable by the server’s user only (chmod 600). - No developer mode.
devModestays off: it exposesPOST /reset, which deletes every event (see Development mode). - A sized queue. Set
maxQueuedWritesfrom how many writes the application sends at once (see Sizing the write queue). - Monitoring. Point a supervisor at
/health, scrape/metrics, and keeplogLevelatwarning(see Health check, Observability, and Logs). - Backups. Schedule
tamarackdb-backup. On the same host, it reads straight from the socket withsourceSocket; from another host, through a reverse proxy that handles TLS (see Backup).
A systemd unit for the server, running as user tamarackdb:
# /etc/systemd/system/tamarackdb.service
[Unit]
Description=TamarackDB
After=network.target
[Service]
User=tamarackdb
Group=tamarackdb
ExecStart=/usr/local/bin/tamarackdb-server --config /etc/tamarackdb/config.toml
Restart=on-failure
# Creates /run/tamarackdb for the socket, and /var/lib/tamarackdb as 0700,
# both owned by the user above.
RuntimeDirectory=tamarackdb
RuntimeDirectoryMode=0755
StateDirectory=tamarackdb
StateDirectoryMode=0700
[Install]
WantedBy=multi-user.targetWith this config.toml, readable by tamarackdb only:
[server]
socketMode = "0660"
dataDir = "/var/lib/tamarackdb"The socket stays at its default path, /run/tamarackdb/tamarackdb.sock, in the directory systemd creates.
To create the user and start the service:
sudo useradd --system --user-group --no-create-home --shell /usr/sbin/nologin tamarackdb
sudo install -d -o tamarackdb -g tamarackdb -m 700 /var/lib/tamarackdb
sudo -u tamarackdb /usr/local/bin/tamarackdb-init --data-dir /var/lib/tamarackdb
sudo systemctl enable --now tamarackdbOn systemctl stop, systemd sends SIGTERM, and the server shuts down in order: it turns away the requests waiting for
their turn, with 503 ShuttingDown, and lets a write already running finish.
Run
Run every command that creates or rewrites files under dataDir as the user the server runs as: the server,
tamarackdb-init, and any sqlite3 you run on the database by hand. The data directory is readable by its owner only,
so a file created by another user, such as root, is one the server can’t open, and it refuses to start. The examples
below assume the server runs as a user named tamarackdb:
Create the default socket’s directory first (see Configuration):
sudo install -d -o tamarackdb -g tamarackdb -m 755 /run/tamarackdb
sudo -u tamarackdb ./bin/tamarackdb-init --data-dir /path/to/data
sudo -u tamarackdb ./bin/tamarackdb-server --config /path/to/config.toml/run is emptied at every reboot, so a directory created by hand is gone the next time the machine starts. For a
lasting service, use the systemd unit in
Production: User=tamarackdb replaces sudo, and
RuntimeDirectory=tamarackdb creates the directory at every start.
tamarackdb-init creates dataDir and a new database in it, with the schema and a new store ID. It refuses to
overwrite an existing database. The server never creates one: when the database file is missing, it refuses to start. A
wrong dataDir, or a disk that isn’t mounted, would otherwise get a new, empty store, and the history would be split in
two.
On every start, the server checks that the database’s schema version matches the one built into the binary, and refuses to start if it doesn’t; it never changes the schema on its own (see Schema). Once running, it logs one line per request to stdout (see Logs).
Docker
Each release publishes an image for linux/amd64 and linux/arm64:
docker run -d -p 127.0.0.1:8085:8085 -v tamarackdb-data:/data ghcr.io/tamarackdb/tamarackdb:latestThe container speaks plain HTTP, with enableAuth off unless you turn it on, so the examples publish its port on
127.0.0.1 only. A plain -p 8085:8085 would publish it on every interface of the host, and give anyone who can reach
the host full access to the API. To expose it to the network, turn enableAuth on (TAMARACKDB_ENABLE_AUTH,
TAMARACKDB_AUTH_TOKEN), and put a reverse proxy in front of it for TLS (see
Security).
Use a version tag, such as ghcr.io/tamarackdb/tamarackdb:v<version>, to pin a release (see the
releases). To build the image from source instead:
docker build -t tamarackdb .
docker run -d -p 127.0.0.1:8085:8085 -v tamarackdb-data:/data tamarackdbThe examples below use the local tamarackdb image; replace it with the published one if that’s what you run.
The image is set up entirely through TAMARACKDB_* environment variables (see
Configuration); no config.toml is needed inside the container. It sets
TAMARACKDB_BIND_ADDRESS=0.0.0.0 and TAMARACKDB_PORT=8085 itself, so it listens over TCP, on port 8085, unlike a
plain tamarackdb-server binary. The unix socket is for a server installed directly on the host (see
Production); in a container, use TCP. It also sets TAMARACKDB_DATA_DIR=/data, so mount a volume on
/data to keep the database across restarts.
The server runs as user and group tamarackdb, UID and GID 10001. A named volume, as above, works as is. To mount a
directory of the host instead, give it to that UID first, readable by it only:
sudo install -d -o 10001 -g 10001 -m 700 /srv/tamarackdb
docker run -d -p 127.0.0.1:8085:8085 -v /srv/tamarackdb:/data tamarackdbWhen /data holds no database, the image runs tamarackdb-init before the server and logs tamarackdb-init: created /data/tamarackdb.sqlite. That line is expected on the first start only. On a restart, it means the container got an
empty volume: check the volume’s name and where it’s mounted.
Alongside the application
When the application runs in a container too, put both on the same Docker network, and publish no port at all: the
application reaches TamarackDB by its service name, and nothing is reachable from outside the host. With docker compose:
services:
tamarackdb:
image: ghcr.io/tamarackdb/tamarackdb:latest
volumes:
- tamarackdb-data:/data
app:
image: my-app
environment:
# The application's own setting, whatever it's named.
TAMARACKDB_URL: http://tamarackdb:8085
volumes:
tamarackdb-data:Every container on that network can reach the API. If the network holds containers you don’t trust, turn enableAuth on
(TAMARACKDB_ENABLE_AUTH and TAMARACKDB_AUTH_TOKEN on the tamarackdb service), and give the token to the
application.
Startup banner
At startup, before opening the store, the server prints a banner and its resolved configuration to stdout: bind address,
port, the socket path and mode, the auth flag (authToken itself is never printed), data directory, development mode,
log level, the pagination, size, write, and queue-depth limits, and the read pool size. Use it to check what an instance
actually runs with. It’s not a machine-readable format.