Tephra on AWS
One small EC2 server with its own disk.
- 1
Grab Tephra
git clone https://github.com/championswimmer/tephra && cd tephra - 2
Launch it
Swap in your own domain. The server, disk, IP and HTTPS are all set up for you.
DOMAIN=notes.yourname.com scripts/deploy/aws.sh - 3
Point your domain at it
The script prints an IP address. At your domain provider, add an
Arecord for your domain pointing to that IP. On Route 53? AddHOSTED_ZONE_ID=…to the command above and this is done for you. - 4
Copy your setup token
Give the server about 5 minutes to warm up. You'll paste the token once, when you create your login.
grep BOOTSTRAP ~/.tephra-aws-notes.yourname.com.env
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.
The /opt/tephra/.env file on the instance (mode 600), loaded by Docker Compose. The CloudFormation path keeps the two secrets in Secrets Manager and writes them into that file at boot.
grep BOOTSTRAP ~/.tephra-aws-notes.yourname.com.env The one-command script sets TEPHRA_PUBLIC_URL and both secrets, and keeps a local copy of the secrets in ~/.tephra-aws-<domain>.env.
| 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. Delete the TEPHRA_BOOTSTRAP_TOKEN line from /opt/tephra/.env and run docker compose up -d in /opt/tephra. Both AWS paths expose only ports 80/443, so add temporary SSH or SSM access first. The bootstrap endpoint is disabled permanently once a user exists.
Want every detail, every knob? The full AWS guide is on GitHub.