Standard Notes is an open-source, and completely encrypted notes app. It offers both free and paid plans and offers both, cloud hosting and the option to host it on your server. You can use your server to sync the notes stored between different devices. Standard Notes offers apps for all desktop operating systems and mobile platforms.
In this tutorial, you will learn how to self-host your standard notes server on a Ubuntu 22.04 machine. You will also learn how to activate paid plan features and file uploads on your self-hosted instance.
Prerequisites
-
A server running Ubuntu 22.04 with a minimum of 2 GB of RAM.
-
A non-root user with sudo privileges.
-
The Uncomplicated Firewall(UFW) is enabled and running.
-
A Fully Qualified domain name pointed to the server. For our tutorial, we will be using the domain
standardnotes.example.com
. You will need another domain name for your file server. We will be using the domainsnotes-files.example.com
. -
Everything is updated.
$ sudo apt update && sudo apt upgrade
Step 1 - Configure Firewall
The first step before installing any packages is to configure the firewall to allow HTTP and HTTPS connections.
Check the status of the firewall.
$ sudo ufw status
You should see something like the following.
Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)
Allow HTTP and HTTPs ports.
$ sudo ufw allow http $ sudo ufw allow https
Check the status again to confirm.
$ sudo ufw status Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 80/tcp (v6) ALLOW Anywhere (v6) 443/tcp (v6) ALLOW Anywhere (v6)
Step 2 - Install Docker and Docker Compose
Add Docker's official GPG key.
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
Run the following command to add the Docker repository.
$ echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Update the system to include Docker's repository.
$ sudo apt update
Install Docker.
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
This tutorial will be using the Docker Compose v2 plugin instead of the older legacy binary. Therefore, the command for running it has changed from docker-compose
to docker compose
and this is reflected here.
Docker runs with elevated privileges so you will need to use sudo
frequently to run commands. The better option is to add your Linux user account to the docker
user group.
$ sudo usermod -aG docker ${USER}
The ${USER}
variable picks up the currently logged-in system account. If you are not logged in with the user you want to give privileges to, replace ${USER}
with the username.
To apply for the new group membership, log out of the server and back in, or use the following command. You will be prompted for the user's password.
$ su - $(USER)
Step 3 - Install Nginx
Ubuntu 22.04 ships with an older version of Nginx. To install the latest version, you need to download the official Nginx repository.
Import Nginx's signing key.
$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Add the repository for Nginx's stable version.
$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \ http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list
Update the system repositories.
$ sudo apt update
Install Nginx.
$ sudo apt install nginx
Verify the installation.
$ nginx -v nginx version: nginx/1.22.0
Step 4 - Install SSL
We need to install Certbot to generate the SSL certificate. You can either install Certbot using Ubuntu's repository or grab the latest version using the Snapd tool. We will be using the Snapd version.
Ubuntu 22.04 comes with Snapd installed by default. Run the following commands to ensure that your version of Snapd is up to date.
$ sudo snap install core
Install Certbot.
$ sudo snap install --classic certbot
Use the following command to ensure that the Certbot command can be run by creating a symbolic link to the /usr/bin
directory.
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Run the following command to generate an SSL Certificate.
$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m name@example.com -d standardnotes.example.com
The above command will download a certificate to the /etc/letsencrypt/live/standardnotes.example.com
directory on your server.
We need to do the same for the Files subdomain.
$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m name@example.com -d snotes-files.example.com
Generate a Diffie-Hellman group certificate.
$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
Open the file /etc/letsencrypt/renewal/standardnotes.example.com.conf
for editing.
$ sudo nano /etc/letsencrypt/renewal/standardnotes.example.com.conf
Paste the following code at the bottom.
pre_hook = systemctl stop nginx post_hook = systemctl start nginx
Save the file by pressing Ctrl + X and entering Y when prompted.
Repeat the same step for the files subdomain by editing the /etc/letsencrypt/renewal/snotes-files.example.com.conf
file.
We have generated the SSL certificate using the standalone option of Certbot. It runs its web server to create the certificate which means Nginx should be shut off during the renewal. The pre_hook and post_hook commands run before and after the renewal to automatically shut and restart the Nginx server thereby requiring no manual intervention.
To check whether the SSL renewal is working fine, do a dry run of the process.
$ sudo certbot renew --dry-run
If you see no errors, you are all set. Your certificate will renew automatically.
Step 5 - Download and Configure Standard Notes
Make sure you are in your system's home directory.
$ cd ~
Clone the Standard Notes Standalone repository.
$ git clone --single-branch --branch main https://github.com/standardnotes/standalone.git
Switch to the downloaded directory.
$ cd standalone
Create the default configuration files for the server.
$ ./server.sh setup
This will create the default environment files which we need to configure. You need to generate six different secret keys. Use the following commands to generate them.
$ openssl rand -hex 32
First, we have to edit the file .env
in the main folder. Open it for editing.
$ nano .env
Change the values of the following variables.
NODE_ENV=production .. AUTH_JWT_SECRET=c0f5bcf6f0f0dcca5b9078c3095e4255a055dfd6376b376733af0e50483cc629 .. DB_USERNAME=std_notes_user DB_PASSWORD=changeme123 .. VALET_TOKEN_SECRET=977c978ca1d5ea22fe2fda65058905b191f724e33db6e47d0a41e034a082cb3b .. FILES_SERVER_URL=https://snotes-files.example.com
Save the file by pressing Ctrl + X and entering Y when prompted.
Next, open the file docker/auth.env
file.
$ nano docker/auth.env
Change the values of the following variables.
JWT_SECRET=54deb1b0b2499e8d875b0d5266dabef9003e13c1787a959a94e339363c10e56e LEGACY_JWT_SECRET=c36aae01803a616213f22422b6d3f998a2beb2cb53af8b95bf578a8a3d046cec .. PSEUDO_KEY_PARAMS_KEY=ea09d3f9122b49c653524cd2285a45fee88beb94f9b76d4d25420b521b080fcd .. ENCRYPTION_SERVER_KEY=04decf379fbe3bb48cf95dbb5997031418b308e724a25d88cb0b2ed6da725efe
Save the file by pressing Ctrl + X and entering Y when prompted.
Step 6 - Start Standard Notes Server
Run the following command to start the server.
$ ./server.sh start
This command will take a few minutes to complete. During this time, the process will pull all the relevant Docker images and create containers for all the services. It will also populate the database and perform appropriate migrations.
You can check the logs for the process using the following command.
$ ./server.sh logs
Press Ctrl + C to exit the logs. You can check the status of the running containers using the following command.
$ ./server.sh status
You will receive a similar output.
Services State: NAME COMMAND SERVICE STATUS PORTS api-gateway-standalone "./wait-for.sh auth …" api-gateway running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp auth-standalone "./wait-for.sh db 33…" auth running auth-worker-standalone "./wait-for.sh db 33…" auth-worker running cache-standalone "docker-entrypoint.s…" cache running 6379/tcp db-standalone "docker-entrypoint.s…" db running 3306/tcp files-standalone "./wait-for.sh db 33…" files running 0.0.0.0:3125->3000/tcp, :::3125->3000/tcp syncing-server-js-standalone "./wait-for.sh db 33…" syncing-server-js running syncing-server-js-worker-standalone "./wait-for.sh db 33…" syncing-server-js-worker running
You can check the health of the server using the following command.
$ curl http://localhost:3000/healthcheck OK
Standard Notes uses port 3000 by default. If you configured a different port in the .env
file, you should update that in the command above.
Step 7 - Configure Nginx
Open the file /etc/nginx/nginx.conf
for editing.
$ sudo nano /etc/nginx/nginx.conf
Add the following line before the line include /etc/nginx/conf.d/*.conf;
.
server_names_hash_bucket_size 64;
Save the file by pressing Ctrl + X and entering Y when prompted.
Create and open the file /etc/nginx/conf.d/standardnotes.conf
for editing.
$ sudo nano /etc/nginx/conf.d/standardnotes.conf
Paste the following code in it. Replace standardnotes.example.com
with your domain name. We have set the value of the client_max_body_size
to 50MB. You can change it as per your requirements.
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name standardnotes.example.com; client_max_body_size 50M; access_log /var/log/nginx/standardnotes.access.log; error_log /var/log/nginx/standardnotes.error.log; ssl_certificate /etc/letsencrypt/live/standardnotes.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/standardnotes.example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/standardnotes.example.com/chain.pem; ssl_session_timeout 5m; ssl_session_cache shared:MozSSL:10m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1; ssl_stapling on; ssl_stapling_verify on; ssl_dhparam /etc/ssl/certs/dhparam.pem; location / { proxy_pass http://127.0.0.1:3000; proxy_cache off; } } # enforce HTTPS server { listen 80; listen [::]:80; server_name standardnotes.example.com; return 301 https://$host$request_uri; }
Save the file by pressing Ctrl + X and entering Y when prompted.
The above file is for the main Standard Notes application. Next, we need to configure another file for the Files subdomain.
$ sudo nano /etc/nginx/conf.d/files-standardnotes.conf
Paste the following code in it. Replace snotes-files.example.com
with your domain name. We have set the value of the client_max_body_size
variable to 50MB. You can change it as per your requirements.
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name snotes-files.example.com; client_max_body_size 50M; access_log /var/log/nginx/files-standardnotes.access.log; error_log /var/log/nginx/files-standardnotes.error.log; ssl_certificate /etc/letsencrypt/live/snotes-files.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/snotes-files.example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/snotes-files.example.com/chain.pem; ssl_session_timeout 5m; ssl_session_cache shared:MozSSL:10m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1; ssl_stapling on; ssl_stapling_verify on; ssl_dhparam /etc/ssl/certs/dhparam.pem; location / { proxy_pass http://127.0.0.1:3125; proxy_cache off; } } # enforce HTTPS server { listen 80; listen [::]:80; server_name snotes-files.example.com; return 301 https://$host$request_uri; }
Verify your Nginx configuration.
$ sudo nginx -t
Restart the Nginx server to enable the configuration files.
$ sudo systemctl restart nginx
Step 8 - Use Standard Notes
If you open the URL https://standardnotes.example.com
in your browser, you should see the following output.
This means your server is up and running. To use Standard Notes, you will need to use the official apps. For our tutorial, we will use their web app but the method will stay the same for the desktop and mobile apps.
Open the URL https://app.standardnotes.com
to access the web app. Click the Create free account link at the bottom left of the page and fill in your email address and password. Click on the Advanced features button and uncheck the option Custom sync server and fill in the URL https://standardnotes.example.com
in the box.
Once signed in, you can start creating notes and using the application.
Step 9 - Enable Paid features
So far, we have enabled basic functionality for the Standard Notes application. The application offers some advanced features like multiple note formats, encrypted cloud storage, longer revision history, and more.
For the cloud-hosted application, you can pay directly to enable advanced features. But for the self-hosted application, you can't pay for the advanced features since the payment application doesn't work. You can donate them though. But to enable paid features in a self-hosted application, you need to run the following command from the Standard Notes directory.
$ cd ~/standardnotes $ bash ./server.sh create-subscription name@example.com
Reload the web application and the paid features should be activated for your account.
Step 10 - Configure the Server for Files upload
File upload is a paid feature of Standard Notes. We have enabled the custom API URL for the file uploads. But they will still not work. To make them work, we need to give proper permissions to the uploads directory. The uploads are stored in the ~/standardnotes/data/uploads
directory. Run the following commands to change permissions.
$ chmod -R 775 data $ mkdir -p data/uploads $ sudo chmod -R 755 data/uploads $ sudo chown -R 1001.1001 data/uploads
Now, Standard Notes sets zero as an upload limit for every user. It means no user can upload files unless given a quota manually. Therefore the final step in making file uploads work is to enable the file quota for the paid user account. We can do that by performing an SQL query inside the database container.
Log in to the MySQL shell inside the Database container.
$ docker exec -it db-standalone mysql -u std_notes_user -p Enter password:
Once inside the MySQL shell, let us check the list of databases.
mysql > show databases; +--------------------+ | Database | +--------------------+ | information_schema | | standard_notes_db | +--------------------+ 2 rows in set (0.00 sec)
Switch to the Standard Notes database.
mysql > use standard_notes_db;
Run the following SQL command to add a 10GB file quota to the paid user-activated above.
mysql> INSERT INTO subscription_settings(uuid, name, value, created_at, updated_at, user_subscription_uuid) VALUES (UUID(), "FILE_UPLOAD_BYTES_LIMIT", 10737418240, FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), (SELECT us.uuid FROM user_subscriptions us INNER JOIN users u ON us.user_uuid=u.uuid WHERE u.email="name@example.com"));
Here 10737418240 refers to total bytes which translates to 10GB. You can modify this number to anything you need.
Exit the MySQL shell and the database container.
mysql > exit
Step 11 - Testing File Uploads
Log in to the Standard Notes web app and click on the attachment icon on the top row.
Click on the Upload files button and select the file you want to upload. The file will upload successfully and you can see it listed by clicking the icon again.
Click on the three dots against the file name to bring up additional options related to the file.
Click the Attach to Note link to add the image to the note. The remaining options are self-explanatory.
Conclusion
This concludes our tutorial on installing and configuring the Standard Notes server on a Ubuntu 22.04 machine. If you have any questions, post them in the comments below.