Getting started
Step 1 · ~10 min

Tephra on DigitalOcean

One Droplet with a Volume attached. Simple and cheap.

You'll need The doctl CLI, signed in with doctl auth initAn SSH key added to your DigitalOcean accountA domain you can add DNS records to
DigitalOcean Droplet reads + writes You browser + Obsidian Your domain notes.yourname.com Reserved IP firewall 80 / 443 Caddy automatic HTTPS env file locked to root Tephra web app + sync API Volume /data · your notes
swipe to see it all → your requests settings it reads where your notes live
  1. 1

    Grab Tephra

    git clone https://github.com/championswimmer/tephra && cd tephra
  2. 2

    Launch it

    Swap in your own domain. The Droplet, Volume, IP and HTTPS are all set up for you.

    DOMAIN=notes.yourname.com ./scripts/deploy/do.sh
  3. 3

    Point your domain at it

    The script prints an IP address. At your domain provider, add an A record for your domain pointing to that IP. Domain already on DigitalOcean? Add DNS_ZONE=yourname.com to the command above and this is done for you.

  4. 4

    Copy your setup token

    Give the server about 5 minutes to warm up. The script prints this exact command with your IP filled in.

    ssh root@YOUR_IP grep BOOTSTRAP /opt/tephra/tephra.env
Reference

Environment variables

The steps above already set everything Tephra needs. This is the complete list for a manual install, a different host, or a change later on.

Where they live on DigitalOcean

/opt/tephra/tephra.env on the Droplet (mode 600), loaded by Docker Compose.

ssh root@YOUR_IP 'grep TEPHRA_ /opt/tephra/tephra.env'

The deploy script generates both secrets and sets TEPHRA_PUBLIC_URL for you.

Variable When Default What it does
TEPHRA_PUBLIC_URL Always none Browser-visible origin, including the scheme (https://notes.yourname.com). Used for redirects, cookies and MCP OAuth discovery. It must match the address people open.
TEPHRA_SESSION_SECRET Always none Signs and protects browser sessions. Use at least 32 random bytes, for example openssl rand -hex 32. Never reuse the bootstrap token here.
TEPHRA_BOOTSTRAP_TOKEN First boot none One-time proof for creating the first admin account at /setup. Delete it from the environment after that account exists.
HOST Optional 0.0.0.0 in the image HTTP bind address. Leave it at 0.0.0.0 inside a container.
PORT Optional 8080 Single port for the API and the web app. Railway injects its own value, so do not set it there.
NODE_ENV Optional production in the image When set to production, session cookies are marked Secure. The container image already sets it.
TEPHRA_DATABASE_DRIVER Optional sqlite Must be sqlite. Tephra refuses to start with any other value: live storage is disk-only.
TEPHRA_SQLITE_PATH Optional /data/tephra.db in the image SQLite database file. Keep it on the persistent disk, never in the container layer.
TEPHRA_BLOB_DRIVER Optional filesystem Must be filesystem, matching the disk-only profile.
TEPHRA_BLOB_PATH Optional /data/blobs in the image Content-addressed attachment store. Must sit next to the database on the same disk.
TEPHRA_MAX_BLOB_BYTES Optional 104857600 (100 MiB) Largest attachment Tephra accepts. Platform request limits can be lower; raise both or neither.
TEPHRA_WEB_ROOT Optional /app/tephra-server/apps/web/dist in the image Directory of the built web app served by the API process. Only change it for custom builds.

Delete TEPHRA_BOOTSTRAP_TOKEN after your first login. It only exists to create the first admin. Run ssh root@YOUR_IP "sed -i '/BOOTSTRAP_TOKEN/d' /opt/tephra/tephra.env && cd /opt/tephra && docker compose --env-file tephra.env up -d".

Want every detail, every knob? The full DigitalOcean guide is on GitHub.

Prefer somewhere else? Railway AWS Google Cloud