Standard Notes is a note-taking app that you can use on all major platforms and mobile devices. With Standard Notes, your notes are end-to-end encrypted and therefore protected from prying eyes.
I’ve been a fan of Standard Notes for a long time. Unfortunately, the annual subscription has always been too expensive for me and I have only used the free version of Standard Notes. Besides the hosted version of Standard Notes, there is also the possibility to install your own self-hosted Standard Notes instance on your server. With all premium features and even for free.
Unfortunately, I haven’t found any instructions on how to set up Standard Notes together with Docker and Traefik as a reverse proxy. Therefore I decided to explain my setup to you.
Selfhosted Standard Notes with Docker and Traefik
Preparation
Info
If you don’t have docker installed yet, you can find instructions for Ubuntu or Debian. This Guide uses docker-compose to run Traefik, therefore its necessary to also install docker-compose. The two linked guides will help you to setup docker-compose on your own host.
Please also make sure that you already have Traefik installed on your server. If you haven’t done that yet, you can find a step by step guide here.
Frist we create a few files and folders to work with. I like my docker setup to be clean and easy, therefore I create a folder for each docker-compose stack I’m running on my host.
mkdir -p /opt/containers/standardnotes
mkdir -p /opt/containers/standardnotes/docker
Now we download the docker-compose sample config provided by Standard Notes.
wget -O /opt/containers/standardnotes/docker-compose.yml https://raw.githubusercontent.com/standardnotes/standalone/main/docker-compose.yml
wget -O /opt/containers/standardnotes/.env https://raw.githubusercontent.com/standardnotes/standalone/main/.env.sample
wget -O /opt/containers/standardnotes/docker/auth.env https://raw.githubusercontent.com/standardnotes/standalone/main/docker/auth.env.sample
wget -O /opt/containers/standardnotes/docker/api-gateway.env https://raw.githubusercontent.com/standardnotes/standalone/main/docker/api-gateway.env.sample
Docker-Compose File
Next up we are changing some lines inside the docker-compose sample file.
vim /opt/containers/standardnotes/docker-compose.yml
To make sure we are always using the latest version of Standard Notes, we need to change the following lines:
image: standardnotes/syncing-server-js:latest
image: standardnotes/syncing-server-js:latest
image: standardnotes/api-gateway:latest
image: standardnotes/auth:latest
image: redis:latest
In the sample config, you will find the exact version instead of “:latest” in each line that needs to be replaced with the above lines.
Traefik Configuration for Standard Notes
vim /opt/containers/standardnotes/docker-compose.yml
And find the following service: api-gateway
You then can delete this service block from your docker-compose.yml file and replace it with the following block.
api-gateway:
image: standardnotes/api-gateway:latest
depends_on:
- auth
- syncing-server-js
env_file: docker/api-gateway.env
environment:
PORT: 3000
AUTH_JWT_SECRET: '${AUTH_JWT_SECRET}'
entrypoint: [
"./wait-for.sh", "auth", "3000",
"./wait-for.sh", "syncing-server-js", "3000",
"./docker/entrypoint.sh", "start-web"
]
networks:
- standardnotes_standalone
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-gateway.entrypoints=http"
- "traefik.http.routers.api-gateway.rule=Host(`notes.yourdomain.tld`)"
- "traefik.http.middlewares.api-gateway-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.api-gateway.middlewares=api-gateway-https-redirect"
- "traefik.http.routers.api-gateway-secure.entrypoints=https"
- "traefik.http.routers.api-gateway-secure.rule=Host(`notes.yourdomain.tld`)"
- "traefik.http.routers.api-gateway-secure.tls=true"
- "traefik.http.routers.api-gateway-secure.tls.certresolver=http"
- "traefik.http.routers.api-gateway-secure.service=api-gateway"
- "traefik.http.services.api-gateway.loadbalancer.server.port=3000"
- "traefik.docker.network=proxy"
It’s important to replace notes.yourdomain.tld
inside the labels
part with your own domain name. Don’t forget to create the appropriate DNS records for your domain.
At the end of our docker-compose.yml File you need to remove the networks:
part and replace it with these lines:
networks:
standardnotes_standalone:
name: standardnotes_standalone
proxy:
external: true
The network must be identical to the network you used for Traefik. In my case it’s proxy
.
If you followed my instructions for installing Traefik, you can simply copy and paste this part.
Your docker-compose.yml
file should now look like this:
version: '3.8'
services:
syncing-server-js:
image: standardnotes/syncing-server-js:latest
depends_on:
- db
- cache
entrypoint: [
"./wait-for.sh", "db", "3306",
"./wait-for.sh", "cache", "6379",
"./docker/entrypoint.sh", "start-web"
]
env_file: .env
environment:
PORT: 3000
restart: unless-stopped
networks:
- standardnotes_standalone
syncing-server-js-worker:
image: standardnotes/syncing-server-js:latest
depends_on:
- db
- cache
- syncing-server-js
entrypoint: [
"./wait-for.sh", "db", "3306",
"./wait-for.sh", "cache", "6379",
"./wait-for.sh", "syncing-server-js", "3000",
"./docker/entrypoint.sh", "start-worker"
]
env_file: .env
environment:
PORT: 3000
restart: unless-stopped
networks:
- standardnotes_standalone
api-gateway:
image: standardnotes/api-gateway:latest
depends_on:
- auth
- syncing-server-js
env_file: docker/api-gateway.env
environment:
PORT: 3000
AUTH_JWT_SECRET: '${AUTH_JWT_SECRET}'
entrypoint: [
"./wait-for.sh", "auth", "3000",
"./wait-for.sh", "syncing-server-js", "3000",
"./docker/entrypoint.sh", "start-web"
]
networks:
- standardnotes_standalone
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-gateway.entrypoints=http"
- "traefik.http.routers.api-gateway.rule=Host(`notes.yourdomain.tld`)"
- "traefik.http.middlewares.api-gateway-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.api-gateway.middlewares=api-gateway-https-redirect"
- "traefik.http.routers.api-gateway-secure.entrypoints=https"
- "traefik.http.routers.api-gateway-secure.rule=Host(`notes.yourdomain.tld`)"
- "traefik.http.routers.api-gateway-secure.tls=true"
- "traefik.http.routers.api-gateway-secure.tls.certresolver=http"
- "traefik.http.routers.api-gateway-secure.service=api-gateway"
- "traefik.http.services.api-gateway.loadbalancer.server.port=3000"
- "traefik.docker.network=proxy"
auth:
image: standardnotes/auth:latest
depends_on:
- db
- cache
- syncing-server-js
entrypoint: [
"./wait-for.sh", "db", "3306",
"./wait-for.sh", "cache", "6379",
"./wait-for.sh", "syncing-server-js", "3000",
"./docker/entrypoint.sh", "start-web"
]
env_file: docker/auth.env
environment:
PORT: 3000
DB_HOST: '${DB_HOST}'
DB_REPLICA_HOST: '${DB_REPLICA_HOST}'
DB_PORT: '${DB_PORT}'
DB_DATABASE: '${DB_DATABASE}'
DB_USERNAME: '${DB_USERNAME}'
DB_PASSWORD: '${DB_PASSWORD}'
DB_DEBUG_LEVEL: '${DB_DEBUG_LEVEL}'
DB_MIGRATIONS_PATH: '${DB_MIGRATIONS_PATH}'
REDIS_URL: '${REDIS_URL}'
AUTH_JWT_SECRET: '${AUTH_JWT_SECRET}'
networks:
- standardnotes_standalone
auth-worker:
image: standardnotes/auth:latest
depends_on:
- db
- cache
- auth
entrypoint: [
"./wait-for.sh", "db", "3306",
"./wait-for.sh", "cache", "6379",
"./wait-for.sh", "auth", "3000",
"./docker/entrypoint.sh", "start-worker"
]
env_file: docker/auth.env
environment:
PORT: 3000
DB_HOST: '${DB_HOST}'
DB_REPLICA_HOST: '${DB_REPLICA_HOST}'
DB_PORT: '${DB_PORT}'
DB_DATABASE: '${DB_DATABASE}'
DB_USERNAME: '${DB_USERNAME}'
DB_PASSWORD: '${DB_PASSWORD}'
DB_DEBUG_LEVEL: '${DB_DEBUG_LEVEL}'
DB_MIGRATIONS_PATH: '${DB_MIGRATIONS_PATH}'
REDIS_URL: '${REDIS_URL}'
AUTH_JWT_SECRET: '${AUTH_JWT_SECRET}'
networks:
- standardnotes_standalone
db:
image: mysql:5.6
environment:
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
ports:
- 3306
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8 --collation-server=utf8_general_ci
volumes:
- ./data/mysql:/var/lib/mysql
- ./data/import:/docker-entrypoint-initdb.d
networks:
- standardnotes_standalone
cache:
image: redis:latest
volumes:
- ./data/redis/:/data
ports:
- 6379
networks:
- standardnotes_standalone
networks:
standardnotes_standalone:
name: standardnotes_standalone
proxy:
external: true
Customize the Environment Files
You may have noticed that in the docker-compose file, many values are specified with a variable. These variables are filled by an environment file.
Now we edit the sample environment files and set our own values.
vim /opt/containers/standardnotes/.env
We need to change the following values.
- AUTH_JWT_SECRET
- DB_PASSWORD
For the first one you need to generate a random string:
openssl rand -hex 32
6d1e6f3dffe6629873e08ab33793fbf80cefbf7c9f8a7de848fa03afc0b3310f
For the DB_PASSWORD
you can use a random and strong Passwort.
At the end, these two lines should like this:
AUTH_JWT_SECRET=6d1e6f3dffe6629873e08ab33793fbf80cefbf7c9f8a7de848fa03afc0b3310f
[...]
DB_PASSWORD=1cd0a6041f4656ff9455fe
Save your changes and go on with the next .env File.
This time we are editing the sample auth.env
.
vim /opt/containers/standardnotes/docker/auth.env.
The following lines need to be changed:
- JWT_SECRET
- LEGACY_JWT_SECRET
- PSEUDO_KEY_PARAMS_KEY
- ENCRYPTION_SERVER_KEY
Please use the openssl command to generate a random string for all four lines.
openssl rand -hex 32
Save your changes. Done.
Start the Standard Notes docker-compose stack
docker-compose -f /opt/containers/standardnotes/docker-compose.yml up -d
You can now visit https://notes.yourdomain.tld/healthcheck
to make sure your installation is working.
It should display a OK
if everything is all right.
Create a Standard Notes Account
Next up you need to create your account on you’r fresh new self-hosted Standard Notes.
The easiest way to do this is to use one of the desktop clients of Standard Notes. I use the Mac client, but you can use whichever client you want.
In the lower left corner of the app, click on Account. A login window will appear. Here we select Register.
Now it is important that you click on Advanced options at the bottom. Here you have to enter your own domain under which your Standard Notes server runs. For example https://notes.yourdomain.tld.
Please make sure that you save your password in a safe place. You will not be able to reset it in the future.
That’s it, from now on you can log in with your account on all devices.
But remember to always specify your own server under Advanced Options, only then the connection to your own instance will be established.