Run your own sync server
Run the Remem sync server with Docker and Postgres behind a TLS reverse proxy, keep it updated and back it up.
Last updated
The self-hosted server is the same sync service as Remem Cloud, as a Rust (axum) server with Postgres 17, run with Docker Compose. Like Remem Cloud, it stores only encrypted items. This is for people comfortable running a server; if that isn’t you, Remem Cloud is the easier choice.
Not public yet
The server’s source code isn’t publicly available yet during the private alpha, so self-hosting is for people we have shared it with. If you would like to self-host, write to hello@remempass.org. The steps below are for them.
Read the security notes first
The self-hosted server lets anyone who can reach it create an account. See Self-hosting security notes before you put it on a network.
What you need
- A machine with Docker and Docker Compose.
- The Remem source code, which we share with you on request during the alpha. The server image is built from it.
- A domain name and a TLS reverse proxy. The server speaks plain HTTP and must never be exposed to the internet directly.
Start the server
- From the top folder of the Remem source we shared with you, create
server/.envwith a long random Postgres password, and the trusted-proxy setting described below. - Start Postgres and the server. The first start builds the server image, which takes a while.
- Check it answers. It listens on
127.0.0.1:8788only, and creates its database tables on start.
# from the top folder of the Remem source
echo "POSTGRES_PASSWORD=$(openssl rand -hex 32)" > server/.env
echo "REMEM_TRUSTED_PROXIES=172.16.0.0/12" >> server/.env
docker compose -f server/docker-compose.yml up -d
curl http://127.0.0.1:8788/healthThe server reads DATABASE_URL, BIND_ADDR, RUST_LOG and REMEM_TRUSTED_PROXIES. The compose file sets the first three and takes the last from server/.env. The database port isn’t published outside Docker. server/README.md in the source describes each setting.
Tell the server which proxy to trust
Sign-in attempts are limited to 10 a minute per client address; an IPv6 client is counted by its /64 block. Behind your reverse proxy, every request reaches the server from the proxy, which with the shipped Docker setup is the Docker gateway, an address in 172.16.0.0/12. Without REMEM_TRUSTED_PROXIES, all your users share one limit, and one person retrying a wrong password can make everyone wait a minute.
REMEM_TRUSTED_PROXIEStakes the addresses or CIDR blocks your own proxies connect from, separated by commas or spaces.docker network inspectshows the exact gateway address if you would rather list only that.- Unset or empty, the server trusts no proxy and counts every request under the address that connected to it.
- Set, it reads
X-Forwarded-Foronly from a trusted address. It reads the header from the right and counts the request under the first readable address that isn’t itself a trusted proxy. If the header is missing, or an unreadable entry comes before any untrusted address, it uses the connecting address. - The server refuses to start if the list doesn’t parse, if it trusts every address (
0.0.0.0/0or::/0; the message says it “must not trust every address”), or if it uses an IPv4-mapped IPv6 block, in which case the message tells you to write it as IPv4. - Your proxy must add to
X-Forwarded-Forrather than pass on what the client sent. Caddy’sreverse_proxydoes this by default; nginx needsproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;.
Trust only proxies you run
Every address on the list can choose which address a request is counted under. Trusting all of 172.16.0.0/12 includes any other container on the same Docker network, so list only the gateway if other containers share it.
Put TLS in front of it
The app only connects to https:// addresses (plain http:// works for localhost only, for testing). Your proxy must also pass WebSocket upgrades on /ws; without them, changes still sync, but a Mac only hears about another device’s changes at its 5-minute check.
For example, with Caddy, which obtains certificates and passes WebSockets by itself:
sync.example.com {
reverse_proxy 127.0.0.1:8788
}
Any proxy works. With nginx, forward the Upgrade and Connection headers for /ws. The server can also live under a path, such as https://example.com/remem/sync, if your proxy strips that prefix before passing requests on: the server itself answers at /. In Caddy, use handle_path /remem/sync/* around the reverse_proxy line.
Update and back up
git pull
docker compose -f server/docker-compose.yml up -d --buildDatabase changes are applied automatically when the server starts. Back up the Postgres database regularly; it holds encrypted items, plus emails, device names and sign-in verifiers, so store backups carefully:
docker compose -f server/docker-compose.yml exec -T postgres pg_dump -U remem remem > remem-backup.sqlWhat about the Cloudflare version?
Remem Cloud runs a second implementation of the server on Cloudflare Workers. Both pass the same conformance tests. Running that one on your own Cloudflare account is possible but isn’t a documented or supported path: its configuration is tied to Remem’s own account and it requires invite codes. Use the Docker server.
Was this missing something? Write to hello@remempass.org.