Author | Nejat Hakan |
nejat.hakan@outlook.de | |
PayPal Me | https://paypal.me/nejathakan |
Self-Hosting Standard Notes
Introduction
Welcome to this comprehensive guide on self-hosting Standard Notes. Standard Notes stands out in the crowded field of note-taking applications due to its strong emphasis on privacy, security, and longevity. It achieves this through robust end-to-end encryption (E2EE) and a commitment to open-source principles. By default, your notes sync through Standard Notes' official servers, which is convenient and secure. However, for users seeking the ultimate level of data control, privacy, and independence from third-party services, self-hosting the Standard Notes server infrastructure is the ideal solution.
Self-hosting means running the server software that powers Standard Notes on hardware you control – whether it's a server in your home, a virtual private server (VPS) in the cloud, or dedicated hardware. The benefits are numerous:
- Complete Data Ownership: Your encrypted notes reside solely on your infrastructure. No third party, not even the Standard Notes developers, has access to your server or data.
- Enhanced Privacy: You control the logs, access policies, and overall operational security of your note-syncing service.
- Customization and Control: While the core server offers limited customization, self-hosting the extensions server allows you to control which features (editors, themes) are available.
- Independence: You are not reliant on the uptime or continued operation of the official Standard Notes servers. Your notes remain accessible as long as your server is running.
- Learning Opportunity: Setting up and maintaining a self-hosted service provides invaluable experience with technologies like Docker, reverse proxies, HTTPS/TLS, server management, and backups.
This guide is structured progressively, starting with the fundamentals and gradually moving towards more complex configurations and maintenance tasks. We will cover:
- Basic Concepts: Understanding the Standard Notes architecture, preparing your server environment, and deploying the core syncing server using Docker.
- Intermediate Setup: Securing your server with HTTPS using a reverse proxy, deploying the extensions server to unlock premium features, and implementing robust data backup strategies.
- Advanced Management: Monitoring server health, performing updates and maintenance, configuring advanced options like email notifications, and troubleshooting common problems.
Each section includes theoretical explanations followed by practical, hands-on workshops designed to reinforce your understanding and provide real-world experience. We assume a baseline familiarity with Linux command-line operations, but we will strive to explain each step clearly and thoroughly, making this guide suitable for university students and anyone eager to dive deep into self-hosting. Let's begin your journey towards digital sovereignty with Standard Notes!
1. Understanding the Standard Notes Ecosystem
Before diving into the technical aspects of self-hosting, it's crucial to understand the different components that make up the Standard Notes ecosystem and how they interact. This foundational knowledge will help you grasp why certain steps are necessary during the setup process.
Standard Notes is designed with a client-server architecture, heavily emphasizing security through end-to-end encryption (E2EE).
Core Components
-
Standard Notes Clients: These are the applications you directly interact with to write, organize, and read your notes. Standard Notes offers clients for various platforms:
- Web App (accessible via any modern web browser)
- Desktop Apps (Windows, macOS, Linux)
- Mobile Apps (iOS, Android) All clients are responsible for encrypting your notes before they leave your device and decrypting them after they are retrieved from the server. The server only ever stores encrypted blobs of data.
-
Standard Notes Server (Syncing Server): This is the central backend component responsible for storing your encrypted notes and synchronizing them across all your connected clients. Key characteristics include:
- Zero-Knowledge: The server has no access to your encryption keys (derived from your password) and therefore cannot decrypt your notes. It only stores and transmits encrypted data.
- Synchronization Logic: It handles the logic for updating notes, resolving conflicts (though rare with the SN protocol), and managing account information (like email and password hash, used for authentication).
- API: Provides an Application Programming Interface (API) that the clients use to send and receive data.
- Self-Hostable: This is the primary component you will deploy when self-hosting. The official open-source implementation is available for anyone to run.
-
Standard Notes Extensions Server (Optional): Standard Notes offers extended functionality beyond basic note-taking through extensions. These include advanced editors (Markdown, Code, Spreadsheets), themes, and other features.
- Serving Extensions: This server component hosts the code for these extensions. When you activate an extension in your client, the client fetches the necessary code from an extensions server.
- Official vs. Self-Hosted: By default, clients use the official Standard Notes extensions server. When self-hosting, you can optionally deploy your own extensions server. This allows you to use paid/extended features without a Standard Notes subscription, provided you host the extensions yourself. Note that the extensions themselves are often open-source but may have specific licensing if used commercially. For personal self-hosted use, this is generally not an issue.
- Configuration: The client needs to be configured to point to your self-hosted extensions server URL if you choose to deploy one.
The Role of Encryption
End-to-end encryption is the cornerstone of Standard Notes' security model. Here's how it works:
- Account Creation: When you create a Standard Notes account, you set a strong password.
- Key Derivation: Your password is used, along with a salt (a random value associated with your account), to derive your master encryption key and authentication key using robust algorithms like PBKDF2. Crucially, this derivation happens entirely on the client-side. Your password is never sent to the server in plaintext.
- Encryption: Every note, tag, or other piece of data you create is encrypted on your device using your encryption key (typically using AES-256) before being sent to the server for synchronization.
- Synchronization: The server receives and stores only the encrypted data blobs. It cannot read the content.
- Decryption: When another client syncs, it downloads the encrypted data blobs. Decryption happens locally on that client device using the keys derived from your password (which you must enter on that device).
This E2EE model ensures that only you, with knowledge of your password, can ever access the content of your notes.
Free vs. Paid Tiers and Self-Hosting
Standard Notes operates on a freemium model:
- Free Tier: Offers basic note-taking functionality, syncing, and access via all clients using the official servers.
- Paid Tier (Extended): Unlocks access to extensions (advanced editors, themes, etc.) when using the official servers. This subscription supports the development and maintenance of the platform.
When you self-host:
- Syncing Server: You can self-host the core syncing server regardless of any subscription. This gives you data ownership and privacy for your basic notes.
- Extensions Server: If you want to use the advanced editors and themes provided by extensions without paying for a Standard Notes subscription, you must also self-host the Extensions server (or the specific components that serve them). Your self-hosted client needs to be pointed to your self-hosted extensions server.
Self-hosting provides a way to access all features through your own infrastructure, but it requires the technical effort of setting up and maintaining both the syncing and extensions servers.
Workshop Setting Up Your First Standard Notes Account
This workshop aims to familiarize you with the standard user experience using the official Standard Notes service. This context is valuable before you embark on self-hosting.
Goal: Create a Standard Notes account, explore the basic client interface, and understand the importance of your password.
Steps:
- Access the Standard Notes Web App:
- Open your web browser and navigate to
https://app.standardnotes.com
.
- Open your web browser and navigate to
- Register a New Account:
- Click on the "Register" or "Create Account" button.
- Enter an email address. This is primarily used for account recovery if you set it up and potentially for password resets on the official service, but it's not strictly necessary for the core function if you manage your password securely.
- Create a very strong password. This password is the only key to decrypt your notes. If you forget this password, your encrypted notes cannot be recovered. Standard Notes staff cannot reset it for you in a way that recovers encrypted data due to the E2EE design.
- Confirm your password.
- Read and agree to the terms and privacy policy.
- Click "Register".
- Initial Tour (Optional):
- The app might offer a brief tour. Take a moment to follow it.
- Explore the Interface:
- Create a Note: Click the "+" icon or "New Note" button. Give it a title and write some content in the main editing area. Notice the simplicity of the default editor.
- Create a Tag: In the left sidebar, find the "Tags" section. Click the "+" icon next to it. Name your tag (e.g.,
testing
,important
). Assign the tag to your newly created note by dragging the tag onto the note in the note list or by editing the note's properties/tags field. - Syncing: If you have another device (e.g., your phone), install the Standard Notes app there and log in with the same email and password. Observe how your test note and tag appear after syncing. Make a change on one device and see it reflected on the other.
- Understand Account Security:
- Go to the "Account" settings menu (usually in the bottom-left corner).
- Look at the security options. Notice the emphasis on the password.
- Find the "Account Key" or "Secret Key". This key is derived from your password and is critical. Standard Notes provides this for backup purposes. Save this key securely (e.g., in a password manager) along with your password. If you ever need to log in and the password derivation changes slightly (e.g., due to software updates), this key might be required.
- Log Out and Log In: Log out of your account and log back in using your email and password to reinforce the process.
Outcome: You now have a basic understanding of the Standard Notes client interface, the synchronization process, and the critical importance of your account password and keys for accessing your encrypted data. This provides essential context for why securing your self-hosted server and managing your credentials carefully is paramount.
2. Preparing Your Server Environment
Before deploying the Standard Notes server software, you need a suitable server environment. This section covers the prerequisites, recommended operating systems, essential software installation, and basic security hardening.
Minimum Server Requirements
Standard Notes server components are relatively lightweight, especially the core syncing server. However, resources required can increase depending on the number of users, the volume of notes, and whether you run the extensions server.
- CPU: 1 vCPU is generally sufficient for personal use or small groups.
- RAM: 512MB is often the bare minimum, but 1GB or more is strongly recommended for stable operation, especially if running Docker and potentially other services like a reverse proxy. 2GB provides more comfortable headroom.
- Disk Space: The Standard Notes database itself is usually small (megabytes to gigabytes unless storing very large notes/files, which isn't the primary use case). However, you need space for the Operating System, Docker images, logs, and potentially backups. 10-20GB of available disk space is a reasonable starting point. SSD storage is recommended for better performance.
- Network: A stable internet connection with sufficient bandwidth for synchronization.
Choosing an Operating System
While the Standard Notes server can potentially run on various operating systems, Linux is the most common and recommended platform for self-hosting, primarily due to its stability, performance, security features, and excellent support for containerization technologies like Docker.
Popular choices include:
- Ubuntu Server (LTS versions like 20.04, 22.04): Widely used, excellent community support, extensive documentation, and regular updates. A great choice for both beginners and experienced users.
- Debian: Known for its stability and adherence to free software principles. Ubuntu is based on Debian. It's another excellent, solid choice.
- CentOS Stream / RHEL / Fedora: Alternatives in the Red Hat ecosystem. Commands for package management (
dnf
/yum
instead ofapt
) and configuration might differ slightly. - Other Distributions: Arch Linux, openSUSE, etc., are also viable but might require more manual configuration.
This guide will primarily use commands compatible with Ubuntu/Debian.
Essential Software Prerequisites Docker and Docker Compose
While you could install the Standard Notes server components directly on the host system, using Docker is highly recommended and the officially supported method for self-hosting.
- What is Docker? Docker is a platform for developing, shipping, and running applications in containers. Containers package an application and its dependencies together, ensuring it runs consistently across different environments.
-
Why Use Docker for Standard Notes?
- Simplified Deployment: Docker abstracts away dependencies. You don't need to manually install specific versions of Node.js, databases, or libraries required by Standard Notes. The Docker image contains everything needed.
- Isolation: The Standard Notes server runs in an isolated container, preventing conflicts with other software on your server.
- Consistency: The application runs the same way regardless of your underlying Linux distribution or configuration.
- Easy Updates: Updating Standard Notes often involves just pulling the latest Docker image and restarting the container.
- Reproducibility: Your deployment can be easily defined and reproduced using configuration files.
-
What is Docker Compose? Docker Compose is a tool for defining and running multi-container Docker applications. You use a YAML file (
docker-compose.yml
) to configure your application's services (like the Standard Notes server, maybe a database, and later the extensions server). With a single command (docker-compose up
), you can create and start all the services defined in your configuration. - Why Use Docker Compose?
- Manages Multi-Container Setups: Simplifies linking containers (e.g., making the extensions server aware of the syncing server).
- Configuration Management: Keeps your entire application stack configuration in one readable file.
- Simplified Commands: Easier than managing individual
docker run
commands with many options.
You will need to install both docker
and docker-compose
(or the docker compose
plugin) on your server.
Basic Server Security Hardening
Before exposing any service to the internet, even one secured with E2EE like Standard Notes, it's crucial to implement basic server security measures.
- Firewall: A firewall controls incoming and outgoing network traffic. You should configure it to allow traffic only on necessary ports.
- SSH (Port 22 by default): Required for remote administration. Access should ideally be restricted to trusted IP addresses if possible, or secured using SSH keys.
- HTTP (Port 80): Needed initially for Let's Encrypt certificate validation if using HTTP-01 challenge.
- HTTPS (Port 443): Required for secure access to your Standard Notes instance via a reverse proxy.
- Standard Notes Port (e.g., 3000): This port should generally not be directly exposed to the internet. It should only be accessible to the reverse proxy running on the same server or within the Docker network.
- UFW (Uncomplicated Firewall): A user-friendly firewall interface for Linux, common on Ubuntu/Debian.
- SSH Key Authentication: Using SSH keys instead of passwords for server login is significantly more secure. Password authentication can be vulnerable to brute-force attacks.
- Regular Updates: Keep your server's operating system and installed packages up-to-date to patch security vulnerabilities. Use
sudo apt update && sudo apt upgrade -y
(on Ubuntu/Debian) regularly. - Fail2Ban (Optional but Recommended): This service scans log files (like SSH logs) and bans IP addresses that show malicious signs, such as too many password failures.
Workshop Preparing a Linux Server for Standard Notes
Goal: Install Docker, Docker Compose, and configure a basic firewall (UFW) on a fresh Ubuntu/Debian server.
Prerequisites: Access to a Linux server (Ubuntu 20.04/22.04 or Debian 11/12 recommended) with sudo
privileges.
Steps:
- Update System Packages:
- Connect to your server via SSH.
- Run the following commands to update the package list and upgrade existing packages:
- Install Docker Engine:
- Follow the official Docker installation guide for your distribution. For Ubuntu/Debian, the general steps are:
- Remove any old Docker versions:
- Set up Docker's
apt
repository:(Note: For Debian, replacesudo apt-get update sudo apt-get install ca-certificates curl gnupg lsb-release -y sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # Use appropriate command for Debian if not Ubuntu echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
ubuntu
withdebian
in the URL and potentially adjust$(lsb_release -cs)
if needed, check Docker docs). - Install Docker Engine:
- Verify Docker installation: (This should download and run a simple container, confirming Docker is working).
- Follow the official Docker installation guide for your distribution. For Ubuntu/Debian, the general steps are:
- Add User to Docker Group (Optional but Recommended):
- To run
docker
commands withoutsudo
, add your user to thedocker
group. Note: This has security implications as it grants privileges equivalent to root. Understand the risks before proceeding. - You need to log out and log back in or run
newgrp docker
for this change to take effect in your current session.
- To run
- Verify Docker Compose Plugin:
- Check if the Docker Compose plugin is installed: (This should output the Docker Compose version).
- Configure Firewall (UFW):
- Check if UFW is installed (it usually is on Ubuntu): (If inactive, proceed. If active, be careful not to lock yourself out).
- Set default policies (deny incoming, allow outgoing):
- Allow essential ports:
- SSH (Critical! Ensure this is correct before enabling UFW):
- HTTP (for Let's Encrypt later):
- HTTPS (for secure access later):
- (We will not allow the Standard Notes default port like 3000 directly, as it will be accessed via the reverse proxy).
- Enable UFW: (Confirm with 'y' if prompted. Double-check you can still SSH in!)
- Verify the rules: (Should show SSH, HTTP, HTTPS allowed).
- Set Up SSH Key Authentication (Highly Recommended - Optional for this Workshop):
- If you haven't already, generate an SSH key pair on your local machine (
ssh-keygen
). - Copy the public key (
~/.ssh/id_rsa.pub
or similar) to your server's~/.ssh/authorized_keys
file. Usessh-copy-id user@your_server_ip
. - Test logging in with the key.
- Once confirmed, you can disable password authentication for better security by editing
/etc/ssh/sshd_config
on the server (PasswordAuthentication no
) and restarting the SSH service (sudo systemctl restart sshd
). Be very careful with this step – ensure key login works perfectly first!
- If you haven't already, generate an SSH key pair on your local machine (
Outcome: Your server now has Docker and Docker Compose installed and running. A basic firewall is configured to allow only necessary ports (SSH, HTTP, HTTPS), providing a more secure foundation for deploying the Standard Notes server. You are ready for the next step: deploying the application itself.
3. Basic Standard Notes Server Deployment (Docker)
With the server environment prepared, you can now deploy the core Standard Notes Syncing Server using Docker. This initial deployment will allow clients to connect directly via the server's IP address, providing a functional, albeit insecure (HTTP), setup for testing and understanding the basics.
The standardnotes/server
Docker Image
The Standard Notes team officially maintains a Docker image named standardnotes/server
. This image contains the necessary code, dependencies (like Node.js), and a simple file-based database system (suitable for many self-hosting scenarios) to run the Syncing Server. You don't need to manually build anything; Docker will pull this pre-built image from Docker Hub.
Configuration via Environment Variables
Docker containers are typically configured using environment variables passed during runtime. The standardnotes/server
image uses several environment variables, but for a basic setup, only a few are strictly necessary. The most critical one is:
SECRET_KEY_BASE
: This is a mandatory secret random string used by the server for signing session cookies and other security-related functions. It's crucial that this remains secret and is sufficiently random. You need to generate a strong random string for this value. A common way is usingopenssl rand -hex 64
.
Other variables exist for database configuration, email setup, etc., which we will explore in later sections.
Deployment using Docker Compose
While you could use a docker run
command, docker-compose
provides a more structured and manageable approach, especially as we add more components later (like the extensions server or a reverse proxy).
Here's a minimal docker-compose.yml
file to run the Syncing Server:
version: '3.7' # Specify docker-compose version
services:
syncserver:
image: standardnotes/server:latest # Use the official image
container_name: standardnotes_syncserver
restart: unless-stopped # Automatically restart if it crashes or server reboots
ports:
- "127.0.0.1:3000:3000" # Map container port 3000 to localhost:3000
volumes:
- ./sn_data:/var/lib/standardnotes # Persist data to a local directory
environment:
# - SECRET_KEY_BASE=YOUR_GENERATED_SECRET_KEY_BASE # Set this securely! See below
# Load environment variables from a .env file for security
- env_file: .env
Explanation:
version: '3.7'
: Defines the version of the Docker Compose file syntax.services:
: Defines the different containers that make up your application.syncserver:
: A custom name for our Standard Notes Syncing Server service.image: standardnotes/server:latest
: Specifies the Docker image to use. Using:latest
is convenient but can sometimes lead to unexpected updates; pinning to a specific version (e.g.,standardnotes/server:3.150.0
) offers more stability.container_name: standardnotes_syncserver
: Assigns a specific name to the running container for easier identification.restart: unless-stopped
: Ensures the container restarts automatically if it stops for any reason other than you manually stopping it.ports:
"127.0.0.1:3000:3000"
: This maps port 3000 inside the container to port 3000 on the host server's localhost interface only (127.0.0.1
). This is crucial for security initially – it prevents direct access from the internet. We will access it through a reverse proxy later. If you needed direct access (e.g., for testing on a local network without a reverse proxy yet), you might temporarily use"3000:3000"
, but this is not recommended for production or internet-facing servers.
volumes:
./sn_data:/var/lib/standardnotes
: This mounts a directory namedsn_data
(relative to where yourdocker-compose.yml
file is located) on your host machine to the/var/lib/standardnotes
directory inside the container. This is where the server stores its data (like the encrypted notes database). This ensures your data persists even if the container is removed and recreated.
environment:
env_file: .env
: Instructs Docker Compose to load environment variables from a file named.env
located in the same directory as thedocker-compose.yml
. This is the recommended way to handle sensitive information likeSECRET_KEY_BASE
.
Handling SECRET_KEY_BASE
Securely
Do not hardcode SECRET_KEY_BASE
directly in your docker-compose.yml
if you plan to commit this file to version control or share it. Use an .env
file:
- Generate the Key: Copy the long hexadecimal string output.
- Create the
.env
file: In the same directory as yourdocker-compose.yml
, create a file named.env
with the following content: Replace the placeholder with the actual key you generated. - Secure the
.env
file: Ensure this file has restrictive permissions (e.g.,chmod 600 .env
) and is added to your.gitignore
file if using Git.
Connecting Clients via IP Address (Initial Test)
Once the server is running, you can configure your Standard Notes client (Web or Desktop recommended for easy configuration) to connect to it.
- Find Server IP: Get the local IP address of your server (e.g., using
ip addr show
orhostname -I
) if accessing from within the same local network, or the public IP if accessing over the internet (though this direct IP access method is temporary and insecure). - Configure Client:
- Open the Standard Notes client.
- Before logging in or registering, look for an "Advanced Options" or similar menu.
- In the "Sync Server" or "Custom Server" field, enter the address:
http://YOUR_SERVER_IP:3000
. Note thehttp
, as we haven't set up HTTPS yet. ReplaceYOUR_SERVER_IP
with the actual IP address. - Save the settings.
- Register/Login: Now, register a new account on your self-hosted server or log in if you previously created one there. Do not use your account from the official service; accounts are specific to the server instance.
Important: Connecting via HTTP sends your login credentials (hashed password) and potentially other metadata unencrypted over the network. This is highly insecure, especially over the internet. This step is purely for initial verification that the server is running. We will secure this with HTTPS in the next section.
Workshop Deploying the Syncing Server via Docker
Goal: Deploy the Standard Notes Syncing Server using Docker Compose and connect a client via the server's IP address for initial testing.
Prerequisites:
- Server prepared according to the previous workshop (Docker, Docker Compose installed).
- Access to the server command line.
- A Standard Notes client (Web or Desktop recommended).
Steps:
- Create a Project Directory:
- On your server, create a dedicated directory for your Standard Notes configuration:
- Generate
SECRET_KEY_BASE
:- Run the command to generate the key:
- Copy the output string.
- Create the
.env
file:- Create and open the
.env
file using a text editor (likenano
): - Add the following line, pasting your generated key:
- Save the file (Ctrl+O in nano, then Enter) and exit (Ctrl+X).
- Create and open the
- Set Permissions for
.env
file:- Restrict permissions:
- Create the
docker-compose.yml
file:- Create and open the
docker-compose.yml
file: - Paste the following content:
Important Security Note: For this workshop only, we are using
version: '3.7' services: syncserver: image: standardnotes/server:latest container_name: standardnotes_syncserver restart: unless-stopped # Temporarily expose port 3000 directly for IP-based testing # WARNING: Insecure for internet exposure! Change back to 127.0.0.1 later. ports: - "3000:3000" volumes: - ./sn_data:/var/lib/standardnotes env_file: - .env
"3000:3000"
to allow direct IP access for testing. In the next section, when setting up the reverse proxy, you must change this back to"127.0.0.1:3000:3000"
. - Save and exit the editor.
- Create and open the
- Start the Container:
- Run Docker Compose in detached mode (
-d
): - Docker Compose will pull the
standardnotes/server
image (if not already present) and start the container.
- Run Docker Compose in detached mode (
- Check Container Status and Logs:
- Verify the container is running:
(Should show
standardnotes_syncserver
with StateUp
). - Check the logs for any errors (press Ctrl+C to stop following): (You should see messages indicating the server started successfully, often listening on port 3000).
- Verify the container is running:
(Should show
- Configure Standard Notes Client:
- Find your server's IP address (e.g.,
hostname -I
or check your cloud provider's dashboard). Let's assume it's192.168.1.100
for this example. - Open the Standard Notes Web App (https://app.standardnotes.com) or the Desktop App.
- Before logging in, click "Advanced Options".
- In the "Sync Server" field, enter:
http://192.168.1.100:3000
(replace192.168.1.100
with your server's actual IP). - Click "Save" or apply the changes.
- Find your server's IP address (e.g.,
- Register a New Account (on your server):
- Back on the login/register screen, choose "Register".
- Use an email (can be fake for local testing, e.g.,
test@local.host
) and a strong password. - Complete the registration.
- Test Synchronization:
- Create a test note (e.g., "My Self-Hosted Note").
- Create a test tag (e.g.,
selfhosted
). - Log out of the client.
- Log back in using the same credentials you just registered on your server.
- Verify that your test note and tag are still present.
Outcome: You have successfully deployed the Standard Notes Syncing Server using Docker Compose and confirmed it's operational by connecting a client directly via IP address. You've created your first account and note on your own server. Remember that this setup is currently insecure (HTTP). The next crucial step is to implement HTTPS using a reverse proxy.
4. Enabling HTTPS with Reverse Proxy
Running your Standard Notes server over HTTP is insecure. Your login credentials (hashed, but still sensitive) and metadata could be intercepted. Enabling HTTPS (HTTP Secure) encrypts the communication between your clients and your server, protecting your data in transit. The standard way to achieve this for containerized applications like Standard Notes is by using a reverse proxy.
The Importance of HTTPS
- Encryption: HTTPS uses TLS/SSL protocols to encrypt all data exchanged between the client (your Standard Notes app) and the server (your reverse proxy). This prevents eavesdropping on public Wi-Fi or by network intermediaries.
- Authentication: The SSL certificate used for HTTPS verifies that the server you are connecting to is genuinely the one associated with the domain name you are using, preventing man-in-the-middle attacks.
- Data Integrity: HTTPS ensures that the data exchanged hasn't been tampered with during transit.
- Browser Requirements: Modern browsers increasingly flag HTTP sites as insecure and may block certain features on non-HTTPS connections.
What is a Reverse Proxy?
A reverse proxy is a server that sits in front of one or more web servers (like your Standard Notes container), intercepting requests from clients. It acts as a gateway or intermediary. For self-hosting Standard Notes, a reverse proxy provides several key benefits:
- SSL/TLS Termination: The reverse proxy handles the complexity of HTTPS encryption and decryption. It receives secure HTTPS requests from clients, decrypts them, and forwards them as plain HTTP requests to the backend service (your Standard Notes container) over the secure internal Docker network or localhost connection. This simplifies the configuration of the backend application, which doesn't need to manage certificates itself.
- Certificate Management: Modern reverse proxies can automatically obtain and renew free SSL/TLS certificates from authorities like Let's Encrypt, significantly simplifying HTTPS setup.
- Centralized Access Point: You can run multiple web services on the same server (e.g., Standard Notes, a blog, a file-sharing app) and use the reverse proxy to route traffic to the correct service based on the requested domain name (e.g.,
notes.yourdomain.com
goes to Standard Notes,blog.yourdomain.com
goes to your blog). - Load Balancing (Advanced): Can distribute incoming traffic across multiple instances of an application for high availability and performance.
- Security: Can provide an additional layer of security, potentially filtering malicious requests, implementing rate limiting, or handling authentication.
Popular Reverse Proxy Options
Several excellent reverse proxy solutions are popular in the self-hosting community, often available as Docker containers:
- Nginx: A powerful, high-performance web server and reverse proxy. Very flexible but can have a steeper learning curve for manual configuration.
- Traefik: A modern reverse proxy designed specifically for Docker and microservices. Features automatic service discovery and configuration based on Docker labels, making it very dynamic. Can be complex initially.
- Caddy: A modern web server with automatic HTTPS via Let's Encrypt built-in by default. Known for its simple configuration file (Caddyfile). A great choice for ease of use, especially regarding HTTPS.
- Nginx Proxy Manager (NPM): This is not Nginx itself, but a user-friendly web GUI built on top of Nginx and Let's Encrypt. It allows managing Nginx proxy configurations, SSL certificates, and access lists through a simple web interface, making it very beginner-friendly.
For this guide, we'll focus on Nginx Proxy Manager (NPM) due to its ease of use for university students and beginners, abstracting away much of the underlying Nginx configuration complexity.
DNS Configuration
To use HTTPS with a proper certificate, you need a domain name (e.g., yourdomain.com
) or a subdomain (e.g., notes.yourdomain.com
).
- Obtain a Domain Name: If you don't have one, you'll need to register one through a domain registrar (e.g., Namecheap, Cloudflare, GoDaddy).
- Configure DNS: You need to create a DNS record (usually an
A
record) that points your chosen domain or subdomain to the public IP address of the server where you are running Standard Notes and the reverse proxy.- Log in to your domain registrar's or DNS provider's control panel.
- Go to the DNS management section for your domain.
- Create a new
A
record:- Host/Name: Enter the subdomain you want to use (e.g.,
notes
if you wantnotes.yourdomain.com
, or@
if you want to use the root domainyourdomain.com
). - Value/Points to: Enter the public IPv4 address of your server.
- TTL (Time To Live): You can usually leave this at the default (e.g., "Automatic" or 1 hour).
- Host/Name: Enter the subdomain you want to use (e.g.,
- Save the record. DNS changes can take some time to propagate (minutes to hours, potentially up to 48 hours in rare cases). You can use tools like
dig
or online DNS checkers (e.g.,dnschecker.org
) to verify propagation.
Nginx Proxy Manager Setup
Nginx Proxy Manager (NPM) itself runs as a Docker container. You'll typically add it to your docker-compose.yml
file or run it as a separate stack.
Example docker-compose.yml
for Nginx Proxy Manager:
version: '3.7'
services:
npm:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx_proxy_manager
restart: unless-stopped
ports:
# Public HTTP port
- '80:80'
# Public HTTPS port
- '443:443'
# Admin GUI port (can be mapped to localhost only for security if desired)
- '8181:81' # Map internal port 81 to host port 8181
volumes:
- ./npm_data:/data # Persist NPM data (configs, users, certs)
- ./letsencrypt:/etc/letsencrypt # Persist Let's Encrypt certificates
environment:
# Set to 1 to allow NPM to manage its own database file within /data
DB_SQLITE_FILE: "/data/database.sqlite"
# Set to 'true' to disable IPv6 if causing issues (optional)
# DISABLE_IPV6: 'true'
networks:
default:
# Optional: define a custom network if needed for communication between NPM and SN
# name: my_shared_network
You would typically run this alongside your Standard Notes docker-compose.yml
, perhaps combining them into one file or ensuring they share a common Docker network so NPM can reach the Standard Notes container by its service name (syncserver
).
Workshop Securing Your Server with Nginx Proxy Manager and Let's Encrypt
Goal: Deploy Nginx Proxy Manager (NPM), configure it to proxy traffic to your Standard Notes container, and obtain a valid Let's Encrypt SSL certificate for HTTPS access.
Prerequisites:
- Standard Notes Syncing Server running via Docker Compose (from the previous workshop).
- A registered domain name.
- Ability to configure DNS records for your domain.
- Server prepared with Docker, Docker Compose, and UFW allowing ports 80, 443, and SSH.
Steps:
- Configure DNS:
- Go to your DNS provider's control panel.
- Create an
A
record pointing your desired subdomain (e.g.,notes.yourdomain.com
) to your server's public IP address. - Wait for DNS propagation (use
ping notes.yourdomain.com
from your local machine ordnschecker.org
to check).
- Modify Standard Notes
docker-compose.yml
:- Crucial Security Step: Edit your
standardnotes/docker-compose.yml
file. - Change the
ports
mapping forsyncserver
back to127.0.0.1:3000:3000
. This prevents direct external access and forces traffic through the reverse proxy. - Optional but Recommended: Define a Network: To ensure NPM can reliably reach the
syncserver
by its service name, define a shared network.version: '3.7' services: syncserver: image: standardnotes/server:latest container_name: standardnotes_syncserver restart: unless-stopped ports: - "127.0.0.1:3000:3000" # Map only to localhost volumes: - ./sn_data:/var/lib/standardnotes env_file: - .env networks: # Add this - sn-network # Add this networks: # Add this whole section sn-network: # Add this name: standardnotes_network # Define a custom network name (optional but good practice)
- Save the file.
- Crucial Security Step: Edit your
- Create Nginx Proxy Manager
docker-compose.yml
:- In a separate directory (e.g.,
mkdir npm && cd npm
) or within yourstandardnotes
directory (if combining), create a newdocker-compose.yml
for NPM. Let's assume you put it inside thestandardnotes
directory and combine them. - Edit your main
docker-compose.yml
to include both services:version: '3.7' services: syncserver: image: standardnotes/server:latest container_name: standardnotes_syncserver restart: unless-stopped ports: - "127.0.0.1:3000:3000" # Map only to localhost volumes: - ./sn_data:/var/lib/standardnotes env_file: - .env networks: - sn-network # Connect to the shared network npm: image: 'jc21/nginx-proxy-manager:latest' container_name: nginx_proxy_manager restart: unless-stopped ports: - '80:80' # Public HTTP port - '443:443' # Public HTTPS port - '8181:81' # Admin GUI on host port 8181 volumes: - ./npm_data:/data - ./letsencrypt:/etc/letsencrypt environment: DB_SQLITE_FILE: "/data/database.sqlite" networks: # Connect to the shared network - sn-network networks: sn-network: name: standardnotes_network
- Save the combined
docker-compose.yml
file.
- In a separate directory (e.g.,
- Restart/Apply Changes:
- Navigate to the directory containing your updated
docker-compose.yml
. - Run
docker compose up -d
. This will recreate thesyncserver
container with the new port mapping and network, and create and start thenpm
container. Usedocker compose down && docker compose up -d
if you encounter issues.
- Navigate to the directory containing your updated
- Access Nginx Proxy Manager Admin UI:
- Open your web browser and navigate to
http://YOUR_SERVER_IP:8181
. - The default login credentials are:
- Email:
admin@example.com
- Password:
changeme
- Email:
- You will be forced to change these immediately upon first login. Use a strong password!
- Open your web browser and navigate to
- Add Proxy Host for Standard Notes:
- Inside the NPM admin interface, navigate to "Hosts" -> "Proxy Hosts".
- Click "Add Proxy Host".
- Fill in the details on the "Details" tab:
- Domain Names: Enter your subdomain (e.g.,
notes.yourdomain.com
). - Scheme: Select
http
. - Forward Hostname / IP: Enter the service name of your Standard Notes container as defined in
docker-compose.yml
(syncserver
). Docker's internal DNS will resolve this if they share a network. Alternatively, you could use the container's internal IP, but the service name is more robust. - Forward Port: Enter the port the Standard Notes container listens on internally, which is
3000
. - Enable
Block Common Exploits
: Recommended. - Enable
Websockets Support
: Important for real-time sync. Toggle this ON.
- Domain Names: Enter your subdomain (e.g.,
- Switch to the "SSL" tab:
- SSL Certificate: Select "Request a new SSL Certificate".
- Enable
Force SSL
: Recommended. This automatically redirects HTTP requests to HTTPS. - Enable
HTTP/2 Support
: Recommended for performance. - Enable
HSTS Enabled
: Recommended for security (Strict Transport Security). This tells browsers to only connect via HTTPS in the future. Be sure HTTPS is working reliably before enabling HSTS Subdomains if you plan to host other things. - Email Address for Let's Encrypt: Enter your valid email address (Let's Encrypt uses this for renewal notices).
- Agree to Let's Encrypt Terms: Toggle the agreement switch.
- Click "Save". NPM will now attempt to contact Let's Encrypt to obtain a certificate. Ensure ports 80 and 443 are open on your server firewall and correctly mapped in the NPM Docker configuration.
- Verify HTTPS Access:
- Wait a minute or two for the certificate process. The status in NPM should change from "Offline" to "Online".
- Open your web browser and navigate to
https://notes.yourdomain.com
(usinghttps
and your actual domain). - You should see a raw text response, likely
Cannot GET /
. This is expected because the root path/
isn't typically used by the SN server API. The important part is that you get a valid HTTPS connection (check the padlock icon in your browser's address bar).
- Reconfigure Standard Notes Client:
- Open your Standard Notes client (Web or Desktop).
- Go back to "Advanced Options".
- Change the "Sync Server" URL to your new HTTPS address:
https://notes.yourdomain.com
(remove the port number, as HTTPS defaults to 443, handled by NPM). - Save the settings.
- Test Login and Sync:
- Log in using the account you created earlier on your self-hosted server.
- Verify that your notes sync correctly over the secure HTTPS connection.
Outcome: You have successfully secured your Standard Notes Syncing Server using Nginx Proxy Manager as a reverse proxy and obtained a valid Let's Encrypt SSL certificate. All communication between your clients and server is now encrypted with HTTPS. You have also configured the components to communicate securely over an internal Docker network.
5. Deploying the Extensions Server
While the core Standard Notes experience is functional with just the Syncing Server, the real power of customization and enhanced productivity comes from Extensions. These include different editors (like Markdown, Code, Spreadsheets), themes, and other tools. To use these features on your self-hosted setup without relying on the official Standard Notes subscription service, you need to self-host the Extensions Server components.
What are Standard Notes Extensions?
Extensions are essentially small web applications (usually built with JavaScript, HTML, CSS) that integrate with the Standard Notes client interface. When you select an extension (e.g., switch to the "Markdown Pro" editor), the client fetches the code for that extension from a designated URL and runs it within the client's sandboxed environment.
The extensions themselves are often open-source and available in repositories maintained by Standard Notes or the community.
The Self-Hosted Extensions Endpoint
To self-host extensions, you need a web server component capable of serving the static files (HTML, JS, CSS) that constitute these extensions. Historically, Standard Notes provided a single standardnotes/extensions
Docker image. However, the architecture has become more modular. Often, self-hosters now deploy a separate, minimal web server (like Nginx or Caddy) specifically configured to serve the extension files, or use specific components provided by the Standard Notes team if available.
A common approach involves:
- Obtaining Extension Code: Cloning the relevant Git repositories containing the extension code (e.g.,
standardnotes/standard-extensions
). - Serving the Files: Using a simple web server container (like
nginx:alpine
) configured to serve the directory containing the extension code. - Reverse Proxy Configuration: Configuring your existing reverse proxy (Nginx Proxy Manager) to route a specific path (e.g.,
/extensions
) or a dedicated subdomain (e.g.,ext.notes.yourdomain.com
) to this new web server container. - Client Configuration: Telling your Standard Notes clients where to find your self-hosted extensions.
Simpler Alternative (Using Pre-built Index):
Some community members or potentially Standard Notes themselves might provide pre-built extension listings or simpler Docker images that bundle common extensions. For this guide, we'll use a method often cited which involves serving a specific index.json
file that points to the locations of individual extensions. The official standardnotes/web
image (the client web app) itself can sometimes be used or adapted for this, or a dedicated simple server.
Let's focus on a pragmatic approach using a simple Nginx container to serve a directory, which is a common and flexible method.
Relationship Between Servers
- The Syncing Server (
syncserver
) handles your note data. - The Extensions Server (our new Nginx container) serves the code for editors, themes, etc.
- The Reverse Proxy (Nginx Proxy Manager) directs traffic:
notes.yourdomain.com
->syncserver:3000
notes.yourdomain.com/ext
(or similar path) ->extensions_server:80
(the Nginx container serving files)
- The Standard Notes Client needs two URLs configured:
- Sync Server URL:
https://notes.yourdomain.com
- Extensions Server URL:
https://notes.yourdomain.com/ext
(or your chosen path/subdomain)
- Sync Server URL:
Configuration Steps
- Prepare Extension Files: You need a directory on your host server containing the extensions you want to serve. The easiest way to start is often by cloning the official extensions repository.
- Add Nginx Service to
docker-compose.yml
: Add a new service for the Nginx container that will serve these files. - Configure Nginx: Provide a basic Nginx configuration file (
nginx.conf
) to tell Nginx how to serve the files (usually just defining the root directory and listening port). - Update Reverse Proxy (NPM): Add a "Location" or custom Nginx configuration within your existing Proxy Host definition in NPM to route the extensions path (e.g.,
/ext
) to the new Nginx container. - Configure SN Client: Update the "Extensions" URL in the Standard Notes client settings.
Workshop Adding Self-Hosted Extensions
Goal: Deploy a simple Nginx container to serve Standard Notes extensions, configure the reverse proxy, and enable custom extensions in the client.
Prerequisites:
- Working self-hosted Standard Notes Syncing Server with HTTPS via Nginx Proxy Manager (from previous workshops).
git
installed on your server (sudo apt install git -y
).- Access to the server command line and NPM web UI.
Steps:
-
Clone Extensions Repository:
- Navigate to your main
standardnotes
project directory on the server. - Clone the official extensions repository (this contains the code and the necessary
index.json
file structure that lists available extensions): - This creates a directory named
extensions-repo
containing the extensions. We will serve the contents of this directory.
- Navigate to your main
-
Create Nginx Configuration for Extensions:
- Create a configuration file for the Nginx container that will serve the extensions. Create a file named
nginx-extensions.conf
in yourstandardnotes
directory: - Paste the following basic Nginx configuration:
server { listen 80; server_name _; # Listen for any server name root /usr/share/nginx/html; # Root directory inside the container index index.html index.htm index.json; # Default files to look for location / { try_files $uri $uri/ =404; } # Add headers to prevent cross-origin issues location ~* \.(?:css|js|json|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { add_header Access-Control-Allow-Origin '*' always; expires 1M; # Cache static assets for 1 month access_log off; log_not_found off; try_files $uri =404; } }
- Save and exit. This configuration tells Nginx to listen on port 80 and serve files from
/usr/share/nginx/html
, adding CORS headers which are often necessary for extensions.
- Create a configuration file for the Nginx container that will serve the extensions. Create a file named
-
Update
docker-compose.yml
:- Edit your main
docker-compose.yml
file to add the newextensions_server
service:Explanation of Changes:version: '3.7' services: syncserver: # ... (syncserver configuration remains the same) ... image: standardnotes/server:latest container_name: standardnotes_syncserver restart: unless-stopped ports: - "127.0.0.1:3000:3000" volumes: - ./sn_data:/var/lib/standardnotes env_file: - .env networks: - sn-network # NEW: Service to serve extensions extensions_server: image: nginx:alpine # Use a lightweight Nginx image container_name: standardnotes_extensions restart: unless-stopped volumes: # Mount the cloned repo into the container's web root - ./extensions-repo:/usr/share/nginx/html:ro # Mount our custom Nginx config - ./nginx-extensions.conf:/etc/nginx/conf.d/default.conf:ro networks: - sn-network # Must be on the same network as NPM npm: # ... (NPM configuration remains the same) ... image: 'jc21/nginx-proxy-manager:latest' container_name: nginx_proxy_manager restart: unless-stopped ports: - '80:80' - '443:443' - '8181:81' volumes: - ./npm_data:/data - ./letsencrypt:/etc/letsencrypt environment: DB_SQLITE_FILE: "/data/database.sqlite" networks: - sn-network networks: sn-network: name: standardnotes_network
- Added a new service
extensions_server
. - Uses the
nginx:alpine
image. - Mounts the
extensions-repo
directory (read-only:ro
) into Nginx's default web root/usr/share/nginx/html
. - Mounts our
nginx-extensions.conf
file (read-only:ro
) to overwrite the default Nginx configuration inside the container. - Connects it to the
sn-network
.
- Added a new service
- Edit your main
-
Apply Docker Compose Changes:
- Run
docker compose up -d
. This will pull thenginx:alpine
image and create/start theextensions_server
container.
- Run
-
Configure Nginx Proxy Manager Location:
- Log in to your NPM Admin UI (
http://YOUR_SERVER_IP:8181
). - Go to "Hosts" -> "Proxy Hosts".
- Click the edit icon for your existing
notes.yourdomain.com
host. - Go to the "Locations" tab.
- Click "Add Location".
- Configure the location:
- Define location URI:
/ext
(This is the path clients will use. You can choose something else, but/ext
is common). - Scheme: Select
http
. - Forward Hostname / IP: Enter the service name:
extensions_server
- Forward Port: Enter the port Nginx listens on inside the container:
80
- Enable
Websockets Support
: Should not be necessary for serving static extension files, leave OFF unless testing proves otherwise.
- Define location URI:
- Click "Save". Now, requests to
https://notes.yourdomain.com/ext/...
will be routed by NPM to yourextensions_server
container.
- Log in to your NPM Admin UI (
-
Configure Standard Notes Client:
- Open your Standard Notes client (Web or Desktop) connected to your self-hosted server (
https://notes.yourdomain.com
). - Go to "Account" -> "Preferences" or "Account" -> "General". Look for the "Advanced Options" or a dedicated "Extensions" section if available.
- Find the field for "Custom Extensions Repository" or "Extensions URL".
- Enter the full URL that points to the root of where your extensions are served by the reverse proxy. Based on our setup, this is the
index.json
file located at the root of theextensions-repo
directory, accessed via the/ext
path we configured. The URL should be:https://notes.yourdomain.com/ext/index.json
- Important: Ensure the URL points directly to the
index.json
file within the path you configured in NPM. - Save the settings. The client might reload or prompt you to.
- Open your Standard Notes client (Web or Desktop) connected to your self-hosted server (
-
Install and Test Extensions:
- Navigate to the "Extensions" section within the Standard Notes client (often in the bottom-left menu).
- You should now see a list of available extensions (themes, editors) loaded from your self-hosted repository. If it's empty or shows an error, double-check the URL, NPM location configuration, and container logs (
docker compose logs extensions_server
). - Try installing a theme (e.g., "Midnight"). Click "Install".
- Try installing an editor (e.g., "Bold Editor" or find one of the Markdown editors listed). Click "Install".
- Activate the theme from the "Appearance" or "Themes" menu.
- Create a new note or edit an existing one. Use the editor switcher (usually at the bottom right or top of the editor pane) to select the newly installed editor.
- Verify that the theme applies correctly and the new editor functions as expected.
Outcome: You have successfully deployed a separate container to serve Standard Notes extensions, configured your reverse proxy to route traffic to it, and connected your Standard Notes client to use your self-hosted extensions repository. You can now use advanced editors, themes, and other extensions without relying on the official Standard Notes subscription service.
6. Data Persistence and Backups
Running a self-hosted service means you are solely responsible for the safety and integrity of your data. While Standard Notes uses robust end-to-end encryption, protecting the encrypted data blobs stored on your server is critical. Hardware failure, accidental deletion, software bugs, or security breaches could lead to data loss if you don't have a reliable backup strategy.
How Data is Stored (Docker Volumes)
In our Docker Compose setup, we used a volume mount to persist the Standard Notes data:
This line tells Docker to map the directory /var/lib/standardnotes
inside the syncserver
container to a directory named sn_data
on the host machine, located in the same directory as the docker-compose.yml
file.
- Host Directory (
./sn_data
): This directory on your server's filesystem contains the actual database files and any other persistent data generated by the Standard Notes server. - Container Directory (
/var/lib/standardnotes
): This is the path the application inside the container uses to read and write its data.
By using this volume mount:
- Data Persists: If you stop and remove the
syncserver
container (docker compose down
), thesn_data
directory on your host remains untouched. When you restart the container (docker compose up -d
), it remounts this directory, and the application finds its previous data. - Data is Accessible: The data is directly accessible on the host filesystem within the
sn_data
directory, which makes backups much easier.
If you had used a named Docker volume instead (e.g., - sn_volume:/var/lib/standardnotes
), the data would be stored in a Docker-managed area on the host (usually /var/lib/docker/volumes/
). Backing up named volumes requires slightly different techniques. Using host directory mounts is often simpler for direct backup access.
Importance of Regular Backups
You need backups to recover from:
- Hardware Failure: Server disk crashes, power surges, etc.
- Data Corruption: Software bugs (in Standard Notes, Docker, or the OS), filesystem errors.
- Accidental Deletion: Mistakes made while managing files or containers.
- Security Incidents: Ransomware, unauthorized access (though E2EE protects note content, account data could be targeted).
- Disaster Recovery: Fire, flood, or other physical disasters affecting your server location.
Remember, RAID is not a backup! RAID protects against single disk failures but not against file deletion, corruption, or disasters.
Backup Strategies for Docker Volumes
Since we mapped the Standard Notes data to the host directory ./sn_data
, backing it up involves backing up that specific directory.
-
Manual Backup (
tar
,rsync
):- You can manually create compressed archives of the data directory.
- Stopping the Container (Recommended for consistency): For the most consistent backup, it's best to stop the Standard Notes container before copying the files to ensure the database is not being written to during the backup process.
- Using
rsync
:rsync
can efficiently copy the directory to another location (e.g., a backup disk or remote server). It only transfers changed files after the initial copy. - Automation: These commands can be placed in a shell script and scheduled using
cron
.
-
Dedicated Backup Tools:
- Use general Linux backup tools like
BorgBackup
,Restic
,Duplicati
, orKopia
. These offer features like deduplication, encryption, compression, and support for various storage backends (local disk, SSH, cloud storage). Configure them to include the./sn_data
directory in their backup sets. They often handle snapshotting or consistency better than simpletar
orrsync
on live data, although stopping the container is still the safest approach for database files.
- Use general Linux backup tools like
-
Volume Backup Utilities (for Named Volumes):
- If you were using named Docker volumes, utilities exist specifically to back them up, often by running a temporary container that mounts the volume and archives its content (e.g.,
loffel/docker-volume-backup
).
- If you were using named Docker volumes, utilities exist specifically to back them up, often by running a temporary container that mounts the volume and archives its content (e.g.,
Backup Frequency and Retention:
- Frequency: Depends on how often your notes change and how much data you can afford to lose. Daily backups are common for actively used note systems.
- Retention: Keep multiple backup copies (e.g., daily for a week, weekly for a month, monthly for a year) to allow recovery from older points in time.
- Location: Store backups in a separate physical location from your primary server (e.g., another computer, external hard drive stored offsite, cloud storage). The 3-2-1 backup rule is a good guideline: 3 copies of your data, on 2 different media types, with 1 copy offsite.
Restoration Process
Restoring from a backup generally involves:
- Stop the Service:
docker compose stop syncserver
(ordocker compose down
). - Remove/Replace Existing Data: Delete or move the current contents of the
./sn_data
directory (or the named volume). - Extract/Copy Backup: Restore the contents of your backup archive or
rsync
backup into the./sn_data
directory. Ensure file permissions and ownership are correct (usually Docker handles this if the volume mount is set up, but worth checking). - Restart the Service:
docker compose start syncserver
(ordocker compose up -d
). - Verify: Check the logs and connect with a client to ensure the data has been restored correctly.
Workshop Implementing a Basic Backup Strategy
Goal: Create a simple shell script to back up the Standard Notes data directory (./sn_data
) to a local backup location and schedule it with cron
.
Prerequisites:
- Working self-hosted Standard Notes setup with data persisted to
./sn_data
. - Access to the server command line with
sudo
privileges (for cron).
Steps:
- Create a Backup Directory:
- Choose a location on your server to store backups. Ideally, this should be a separate physical disk or mount point. For this workshop, we'll create a directory in the user's home directory (adjust as needed for a real setup).
- Create the Backup Script:
- Navigate to a suitable location for scripts, e.g., your home directory or
/usr/local/bin
. - Paste the following script content:
#!/bin/bash # --- Configuration --- # Path to the directory containing your docker-compose.yml SN_COMPOSE_DIR="/path/to/your/standardnotes" # Path to the Standard Notes data directory (relative to SN_COMPOSE_DIR) SN_DATA_DIR="sn_data" # Path where backups should be stored BACKUP_DIR="/home/YOUR_USERNAME/sn_backups" # <-- CHANGE YOUR_USERNAME # Number of days to keep backups RETENTION_DAYS=7 # --- End Configuration --- # Create timestamp TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILENAME="standardnotes_backup_${TIMESTAMP}.tar.gz" BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILENAME}" echo "Starting Standard Notes backup..." # Navigate to the docker-compose directory cd "${SN_COMPOSE_DIR}" || { echo "Error: Cannot cd to ${SN_COMPOSE_DIR}"; exit 1; } # Stop the Standard Notes container for data consistency echo "Stopping syncserver container..." docker compose stop syncserver if [ $? -ne 0 ]; then echo "Error: Failed to stop syncserver. Aborting backup." exit 1 fi # Create the compressed archive echo "Creating backup archive: ${BACKUP_PATH}" tar czf "${BACKUP_PATH}" "${SN_DATA_DIR}" if [ $? -ne 0 ]; then echo "Error: Failed to create tar archive. Check permissions and disk space." # Attempt to restart container even if backup failed docker compose start syncserver exit 1 fi # Restart the Standard Notes container echo "Starting syncserver container..." docker compose start syncserver if [ $? -ne 0 ]; then echo "Error: Failed to restart syncserver after backup." # Backup was created, but investigate container startup issue exit 1 fi # Prune old backups echo "Pruning backups older than ${RETENTION_DAYS} days in ${BACKUP_DIR}..." find "${BACKUP_DIR}" -name "standardnotes_backup_*.tar.gz" -type f -mtime +${RETENTION_DAYS} -delete echo "Backup completed successfully: ${BACKUP_PATH}" exit 0
- Crucially:
- Replace
/path/to/your/standardnotes
with the actual absolute path to the directory containing yourdocker-compose.yml
file. - Replace
YOUR_USERNAME
with your actual username in theBACKUP_DIR
path, or change the path entirely if desired.
- Replace
- Save and exit (
Ctrl+O
, Enter,Ctrl+X
in nano).
- Navigate to a suitable location for scripts, e.g., your home directory or
- Make the Script Executable:
bash chmod +x backup_standardnotes.sh
- Perform a Manual Test Run:
- Execute the script directly:
- Check for any errors in the output.
- Verify that a
.tar.gz
file was created in your~/sn_backups
directory. - Check that the
syncserver
container was stopped and restarted (docker compose ps
).
- Schedule with Cron:
- Open the cron table for editing (usually runs jobs as your user, use
sudo crontab -e
to run as root if needed, but ensure permissions are handled): (Select an editor like nano if prompted for the first time). - Add a line to schedule the script. For example, to run it every day at 3:00 AM:
# Example: Run Standard Notes backup daily at 3:00 AM 0 3 * * * /home/YOUR_USERNAME/backup_standardnotes.sh >> /home/YOUR_USERNAME/sn_backups/backup.log 2>&1
- Replace
YOUR_USERNAME
with your actual username and ensure the path to the script is correct. 0 3 * * *
: Cron timing (minute 0, hour 3, any day of month, any month, any day of week).>> /home/YOUR_USERNAME/sn_backups/backup.log 2>&1
: This redirects both standard output (>>
) and standard error (2>&1
) to a log file, appending (>>
) each time the script runs. This is useful for checking if the backups ran successfully later.
- Replace
- Save and exit the crontab editor. Cron will automatically pick up the schedule.
- Open the cron table for editing (usually runs jobs as your user, use
- Verify Cron Setup (Optional):
- Check that the cron job is listed:
crontab -l
. - After the scheduled time passes, check the
~/sn_backups
directory for new backups and examine thebackup.log
file for output.
- Check that the cron job is listed:
Outcome: You have created and scheduled a basic automated backup script for your Standard Notes data. While this script provides a fundamental level of protection, remember to enhance it for a production environment by storing backups off-server and potentially using more sophisticated backup tools. You now understand the process of backing up data persisted via Docker host mounts.
7. Server Monitoring and Maintenance
Self-hosting is not a "set it and forget it" affair. Regular monitoring and maintenance are essential to ensure your Standard Notes service remains available, performant, and secure. This involves keeping an eye on server resources, checking application logs, and applying updates in a timely manner.
Importance of Monitoring
Monitoring helps you:
- Detect Problems Early: Identify issues like low disk space, high CPU usage, or application errors before they cause outages.
- Understand Resource Usage: Determine if your server specifications are adequate or if you need to upgrade.
- Ensure Availability: Confirm that your Standard Notes server and related services (like the reverse proxy) are running and accessible.
- Troubleshoot Performance: Pinpoint bottlenecks by observing resource utilization patterns.
- Security: Unusual resource usage or log entries can sometimes indicate security issues.
Key Areas to Monitor
- System Resources:
- CPU Usage: High sustained CPU usage might indicate performance issues or runaway processes.
- RAM Usage: Running out of memory can cause instability or crashes. Monitor swap usage as well (high swap usage often indicates insufficient RAM).
- Disk Space: Crucial for storing the OS, application data, Docker images, logs, and backups. Running out of disk space will cause failures. Monitor the usage of the filesystem where your Docker data (
./sn_data
,./npm_data
, etc.) resides. - Network I/O: Monitor bandwidth usage, especially if you have data caps or notice slow synchronization.
- Container Health:
- Status: Are the
standardnotes_syncserver
,extensions_server
, andnginx_proxy_manager
containers running? (docker compose ps
) - Resource Consumption: How much CPU and RAM are the individual containers using? (
docker stats
)
- Status: Are the
- Application Logs:
- Standard Notes Server Logs: Check for errors, warnings, or unusual patterns (
docker compose logs syncserver
). - Nginx Proxy Manager Logs: Check for access patterns, HTTP errors (like 5xx server errors or 4xx client errors), and SSL certificate issues (
docker compose logs npm
). - Extensions Server Logs: Check for errors serving extension files (
docker compose logs extensions_server
).
- Standard Notes Server Logs: Check for errors, warnings, or unusual patterns (
- Service Availability:
- HTTPS Endpoint: Is your Standard Notes URL (
https://notes.yourdomain.com
) reachable and returning a valid response (even if it's just theCannot GET /
message)? Is the SSL certificate valid? - NPM Admin UI: Is the Nginx Proxy Manager UI accessible?
- HTTPS Endpoint: Is your Standard Notes URL (
Monitoring Tools
- Command-Line Tools (Built-in):
top
/htop
: Real-time system resource monitoring (CPU, RAM, processes).htop
is more user-friendly (sudo apt install htop
).df -h
: Check disk space usage (-h
for human-readable).free -h
: Check RAM and swap usage.docker stats
: Real-time resource usage per container.docker compose logs [-f] [service_name]
: View container logs (-f
to follow).
- Dedicated Monitoring Applications:
- Uptime Kuma: A popular, easy-to-use, self-hostable monitoring tool. It provides a web UI to monitor HTTP(S) endpoints, ports, Docker containers, and more, with notifications. Excellent for availability monitoring.
- Prometheus + Grafana: A powerful combination for collecting time-series metrics (Prometheus) and visualizing them in dashboards (Grafana). Can monitor system resources (using
node_exporter
), Docker stats (cadvisor
), and application-specific metrics. Steeper learning curve but very comprehensive. - Netdata: Real-time performance monitoring with auto-discovery and detailed charts. Can run as a Docker container.
Performing Updates
Keeping your software up-to-date is crucial for security and access to new features or bug fixes.
- System Updates: Regularly update your host operating system:
- Docker Container Updates (Using Docker Compose):
- Check Release Notes: Before updating, always check the release notes or changelog for the Docker images you are using (
standardnotes/server
,jc21/nginx-proxy-manager
,nginx
). Look for any breaking changes or specific upgrade instructions. - Pull Latest Images: Navigate to your
docker-compose.yml
directory and run: (This downloads the newer versions of the images specified in your compose file, if available). - Recreate Containers: Apply the updates by recreating the containers: (Docker Compose is smart enough to only recreate containers whose images have changed).
- Verify: Check logs and test functionality after updating.
- Check Release Notes: Before updating, always check the release notes or changelog for the Docker images you are using (
- Prune Old Docker Resources (Optional): Over time, unused Docker images, networks, and volumes can consume disk space. Periodically clean them up:
Handling Breaking Changes
Sometimes, updates (especially major version bumps) might introduce changes that are incompatible with your current configuration or data format.
- Read Release Notes: This is the most important step. Developers usually document breaking changes.
- Backup Before Updating: Always ensure you have a recent, restorable backup before applying major updates.
- Test in Staging (Advanced): For critical systems, consider setting up a separate staging environment to test updates before applying them to your production server.
- Pin Image Versions: In your
docker-compose.yml
, you can specify exact image versions (e.g.,standardnotes/server:3.150.0
) instead oflatest
. This prevents automatic updates to potentially incompatible versions when you rundocker compose pull
, giving you control over when you upgrade. Update the version tag manually when you are ready to upgrade.
Workshop Setting Up Uptime Kuma for Monitoring
Goal: Deploy Uptime Kuma using Docker Compose and configure it to monitor the availability of your self-hosted Standard Notes service and the server itself.
Prerequisites:
- Working self-hosted Standard Notes setup with HTTPS.
- Server with Docker and Docker Compose.
- Port 3001 (or another chosen port) available on the host for Uptime Kuma's UI.
Steps:
- Create Uptime Kuma
docker-compose.yml
:- You can add Uptime Kuma to your existing
standardnotes/docker-compose.yml
or run it separately. Let's add it to the existing file for simplicity, ensuring it's on the same network is not strictly necessary for basic HTTP checks but doesn't hurt. - Edit your
docker-compose.yml
:version: '3.7' services: syncserver: # ... (syncserver configuration) ... networks: - sn-network extensions_server: # ... (extensions_server configuration) ... networks: - sn-network npm: # ... (NPM configuration) ... networks: - sn-network # NEW: Uptime Kuma service uptime-kuma: image: louislam/uptime-kuma:latest container_name: uptime_kuma volumes: - ./uptime_kuma_data:/app/data # Persist Uptime Kuma data ports: - "3001:3001" # Map internal port 3001 to host port 3001 restart: unless-stopped networks: # Optional: can connect to sn-network if monitoring internal services by name - sn-network networks: sn-network: name: standardnotes_network
- Note: We map host port 3001 to the container's port 3001. Ensure port 3001 is allowed through your firewall if you want to access the Uptime Kuma UI from outside your local network (e.g.,
sudo ufw allow 3001/tcp
). For internal access only, you could map it like- "127.0.0.1:3001:3001"
. - Save the file.
- You can add Uptime Kuma to your existing
- Start Uptime Kuma:
- Run
docker compose up -d
. This will pull the Uptime Kuma image and start the container.
- Run
- Access Uptime Kuma UI:
- Open your web browser and navigate to
http://YOUR_SERVER_IP:3001
. - Create an administrator account when prompted.
- Open your web browser and navigate to
- Add Monitor for Standard Notes HTTPS Endpoint:
- Click "+ Add New Monitor".
- Monitor Type: Select
HTTP(s)
. - Friendly Name:
Standard Notes Server
- URL: Enter your full HTTPS URL:
https://notes.yourdomain.com
- Check Interval: Adjust as needed (e.g., 60 seconds).
- Accepted Status Codes: Leave default
200-299
. Even though SN root returnsCannot GET /
(which might be a 404), the HTTPS connection itself succeeds. You might need to adjust this if Uptime Kuma flags it down due to the 404. Alternatively, monitor a known API path if one exists that returns 200, or simply rely on the successful TLS handshake. A better check might be to monitor the specific sync endpoint if known, e.g.,https://notes.yourdomain.com/items/sync
, but this might require authentication or specific request types Uptime Kuma can't easily do. For basic availability, checking the domain itself is often sufficient. You can also enable "Ignore TLS/SSL error for HTTPS websites" temporarily for debugging, but it should be off for production monitoring. - Configure Notifications (Optional): Set up email, Discord, Telegram, etc., notifications if desired.
- Click "Save".
- Add Monitor for Server Ping (Basic):
- Click "+ Add New Monitor".
- Monitor Type: Select
Ping
. - Friendly Name:
My Server (Ping)
- Hostname / IP: Enter your server's public IP address or a domain name pointing to it.
- Click "Save".
- Add Monitor for SSH Port (Optional):
- Click "+ Add New Monitor".
- Monitor Type: Select
TCP Port
. - Friendly Name:
Server SSH Port
- Hostname / IP: Enter your server's IP or domain.
- Port: Enter
22
(or your custom SSH port). - Click "Save".
- Observe Monitoring:
- Go back to the Uptime Kuma dashboard. You should see your monitors listed.
- Initially, they will be gray (pending), then should turn green (up) if everything is configured correctly and the services are reachable. If they turn red (down), investigate the error message provided by Uptime Kuma.
Outcome: You have deployed Uptime Kuma and configured basic monitors to track the availability of your Standard Notes HTTPS endpoint and the server itself via Ping/TCP checks. This provides automated checks and a visual dashboard to quickly assess the health of your service. Explore Uptime Kuma's other monitor types (like Docker Container monitoring) and notification options for more comprehensive oversight.
8. Advanced Configuration and Customization
Beyond the basic setup and extensions, the Standard Notes server offers further configuration options for tailoring its behavior, integrating with email services, and potentially using different database backends (though this adds significant complexity).
Exploring Environment Variables
The standardnotes/server
Docker image accepts various environment variables beyond the essential SECRET_KEY_BASE
. You can find a list in the official documentation or sometimes by inspecting the image's Dockerfile or startup scripts. Some potentially useful ones include:
- Database Configuration:
DB_CONNECTION
: Specifies the database type. Defaults tosqlite
. Can potentially be set topostgres
ormysql
.DB_HOST
,DB_PORT
,DB_DATABASE
,DB_USERNAME
,DB_PASSWORD
: Required if using PostgreSQL or MySQL instead of the default SQLite. Note: Setting up and managing an external database significantly increases complexity (requires deploying/managing the database server, networking, backups). SQLite is often sufficient and much simpler for personal or small-group use.
- Email Configuration (for Password Resets, etc.):
EMAIL_HOST
: SMTP server hostname (e.g.,smtp.gmail.com
,smtp.mailgun.org
).EMAIL_PORT
: SMTP server port (e.g., 587 for TLS, 465 for SSL).EMAIL_HOST_USER
: SMTP username.EMAIL_HOST_PASSWORD
: SMTP password (use Docker secrets or secure methods).EMAIL_FROM
: The "From" email address for outgoing emails (e.g.,no-reply@notes.yourdomain.com
).EMAIL_SECURE
: Set totrue
if using port 465 (SSL), oftenfalse
or omitted for port 587 (STARTTLS). Check your provider's requirements.EMAIL_IGNORE_TLS
,EMAIL_REQUIRE_TLS
: Fine-tuning TLS requirements.
- Rate Limiting (Advanced): Variables might exist to control API request limits, helping prevent abuse. Check documentation for specifics.
- Other Options: Settings related to file uploads (if using FileSafe - usually requires S3-compatible storage), logging levels, etc., may be available.
Always consult the official Standard Notes server documentation for the most accurate and up-to-date list of supported environment variables and their usage.
Using Alternative Database Backends (PostgreSQL/MySQL)
While the default SQLite backend (stored in the /var/lib/standardnotes
volume) is simple and performs well for many use cases, you might consider PostgreSQL or MySQL if:
- You anticipate a very large number of users or notes, potentially exceeding SQLite's practical limits for concurrent writes.
- You already have an existing, managed PostgreSQL or MySQL server that you want to leverage.
- You require advanced database features or replication capabilities offered by these systems.
Complexity Considerations:
- Deployment: You need to deploy and manage the PostgreSQL or MySQL server itself (often as another Docker container or a separate service).
- Networking: Ensure the Standard Notes container can securely connect to the database container/server.
- Configuration: Set the
DB_*
environment variables correctly in your Standard Notesdocker-compose.yml
. - Backup: You now need to back up the external database in addition to any file-based volumes used by Standard Notes. Database backups often require specific tools (
pg_dump
,mysqldump
). - Migration: Migrating existing data from SQLite to PostgreSQL/MySQL can be complex.
Recommendation: Stick with the default SQLite backend unless you have a compelling, specific reason and the technical expertise to manage an external database system reliably.
Setting Up Email Functionality
Configuring email allows your self-hosted server to send notifications, most importantly password reset emails. Without this, if a user forgets their password, there is no self-service recovery mechanism on your instance.
Steps:
- Obtain SMTP Credentials: You need access to an SMTP server. Options include:
- Transactional Email Services: Mailgun, SendGrid, Amazon SES, Postmark. Often have free tiers suitable for low-volume personal use. Recommended for reliability.
- Regular Email Providers: Gmail, Outlook, etc. May work, but often have stricter sending limits and might require enabling "less secure app access" or generating app-specific passwords, making them less ideal for server applications.
- Self-Hosted Email Server: If you run your own email server (e.g., Mailcow, Poste.io), you can use its SMTP credentials. Get the Hostname, Port, Username, and Password for your chosen SMTP service.
- Configure Environment Variables: Add the
EMAIL_*
variables to your.env
file or directly under theenvironment:
section for thesyncserver
indocker-compose.yml
(using Docker secrets for the password is best practice if available). - Restart Container: Run
docker compose up -d
to apply the new environment variables. - Test: Use the "Forgot Password" link on your self-hosted Standard Notes login page (
https://notes.yourdomain.com
) with an existing account's email address. Check if the email arrives. Debug using container logs (docker compose logs syncserver
) if emails fail to send.
Customizing Default Extensions
When you self-host the extensions server (as done in Workshop 5), the list of extensions presented in the client is determined by the index.json
file located at the root of the served directory (e.g., ./extensions-repo/index.json
).
You can potentially customize this list:
- Editing
index.json
: Carefully edit theindex.json
file within your./extensions-repo
directory. You could remove entries for extensions you don't want to offer or potentially add entries for third-party or custom extensions (if you host their code correctly). Be mindful of the file format and theurl
field for each extension, which must point to the correct manifest file (usually another.json
file) for that specific extension within the served directory structure. - Filtering Repositories: Instead of cloning the entire
standard-extensions
repo, you could selectively clone or copy only the specific extension directories you want and construct a simplerindex.json
file manually.
Caution: Modifying the extensions list requires understanding the structure and ensuring the paths in index.json
correctly resolve to the actual extension manifest files served by your extensions_server
container. Incorrect modifications can break the extensions feature.
Workshop Enabling Email Password Resets
Goal: Configure the Standard Notes server to send password reset emails using a transactional email service (Mailgun example, adapt for others).
Prerequisites:
- Working self-hosted Standard Notes setup with HTTPS.
- Account with an SMTP provider (e.g., Mailgun - free tier available). You'll need your SMTP Hostname, Port (usually 587), Username, and Password.
- An existing user account created on your self-hosted server with a valid email address associated with it.
Steps:
- Get SMTP Credentials from Provider:
- Sign up for a service like Mailgun. You might need to verify your domain.
- Navigate to the SMTP credentials section in your provider's dashboard. Note down:
- SMTP Hostname (e.g.,
smtp.mailgun.org
) - Port (e.g.,
587
for TLS/STARTTLS) - SMTP Username (e.g.,
postmaster@sandbox...mgsend.net
or your custom domain user) - SMTP Password (generate one specifically for this use).
- SMTP Hostname (e.g.,
- Update
.env
File:- Edit the
.env
file in yourstandardnotes
project directory: - Add the following lines, replacing the placeholder values with your actual credentials:
# Existing SECRET_KEY_BASE=... # Email Configuration EMAIL_HOST=smtp.mailgun.org # <-- Replace with your SMTP host EMAIL_PORT=587 # <-- Replace with your SMTP port (587 for TLS, 465 for SSL) EMAIL_HOST_USER=your_smtp_username # <-- Replace with your SMTP username EMAIL_HOST_PASSWORD=your_smtp_password # <-- Replace with your SMTP password EMAIL_FROM=noreply@notes.yourdomain.com # <-- Replace with a desired 'From' address EMAIL_SECURE=false # Set to true ONLY if using port 465 (SSL), false/omit for 587 (TLS/STARTTLS) # EMAIL_REQUIRE_TLS=true # Often needed for port 587
- Adjust
EMAIL_SECURE
and potentially addEMAIL_REQUIRE_TLS=true
based on your provider's documentation for the chosen port. Port 587 typically uses STARTTLS (EMAIL_SECURE=false
,EMAIL_REQUIRE_TLS=true
). Port 465 uses direct SSL (EMAIL_SECURE=true
).
- Adjust
- Save and exit.
- Edit the
- Ensure
env_file
is used indocker-compose.yml
:- Double-check that your
syncserver
service definition indocker-compose.yml
includes the lineenv_file: - .env
.
- Double-check that your
- Restart Standard Notes Server:
- Apply the configuration changes:
(Using
--force-recreate
ensures the container restarts and picks up the new environment variables from the.env
file).
- Apply the configuration changes:
(Using
- Check Logs (Optional):
- Monitor the logs immediately after restart to catch any obvious configuration errors related to email:
- Test Password Reset:
- Open your Standard Notes instance in a browser:
https://notes.yourdomain.com
. - Log out if you are currently logged in.
- Click the "Log in" button.
- Click the "Forgot password?" link.
- Enter the email address associated with an existing account on your self-hosted server.
- Click "Send Email".
- Check your email inbox (and spam folder) for the password reset email from the
EMAIL_FROM
address you configured. - Follow the instructions in the email to reset the password.
- Try logging in with the new password.
- Open your Standard Notes instance in a browser:
Outcome: You have successfully configured your self-hosted Standard Notes server to send emails via SMTP. Users who forget their passwords can now use the self-service password reset functionality, improving the usability of your instance. If emails are not sending, carefully review the SMTP settings, credentials, and the syncserver
logs for specific error messages.
9. Troubleshooting Common Issues
Even with careful setup, you might encounter issues when running your self-hosted Standard Notes instance. This section covers common problems and provides a systematic approach to diagnosing and resolving them.
General Troubleshooting Strategy
- Identify the Symptom: What exactly is not working? (e.g., Cannot connect, notes not syncing, extensions not loading, container crashing). Be specific.
- Check Logs: This is almost always the first step. Examine the logs of the relevant containers (
syncserver
,npm
,extensions_server
). Look for error messages, warnings, stack traces, or clues around the time the issue occurred. - Verify Container Status: Are all necessary containers running? If a container is stopped or restarting, investigate its logs.
- Check Network Connectivity:
- DNS: Can your client machine and the server itself resolve the domain name (
notes.yourdomain.com
) correctly? Useping
andnslookup
(ordig
). - Firewall: Are the necessary ports (usually 80, 443 for NPM; SSH for access) open on the server's firewall (
sudo ufw status
)? - Reverse Proxy: Is NPM running and configured correctly? Check its logs. Can you access the NPM admin UI?
- Docker Network: Are the containers connected to the shared Docker network (
docker network inspect standardnotes_network
)? Can containers ping each other by service name within the network (usedocker exec -it <container_name> ping <other_service_name>
)?
- DNS: Can your client machine and the server itself resolve the domain name (
- Check Resource Usage: Is the server running out of CPU, RAM, or disk space?
- Configuration Review: Double-check your
docker-compose.yml
,.env
file, NPM proxy host settings, and Standard Notes client settings (Sync URL, Extensions URL) for typos or misconfigurations. - Restart Containers: Sometimes a simple restart can resolve temporary glitches.
- Search Online: Use specific error messages from logs to search online forums, GitHub issues for Standard Notes Server, Nginx Proxy Manager, or Docker. Someone else may have encountered the same problem.
Specific Issues and Solutions
- Cannot Connect to
https://notes.yourdomain.com
(Timeout/Connection Refused)- DNS: Verify DNS propagation using
ping notes.yourdomain.com
ordnschecker.org
. Ensure the A record points to the correct public IP. - Firewall: Check if ports 80 and 443 are allowed in UFW (
sudo ufw status
). - NPM Container: Is the
npm
container running (docker compose ps
)? Are ports 80 and 443 correctly mapped indocker-compose.yml
? Check NPM logs (docker compose logs npm
). - Server Reachability: Can you ping the server's IP address?
- DNS: Verify DNS propagation using
- Getting 502 Bad Gateway Error
- This usually means the reverse proxy (NPM) successfully received the request but could not connect to the backend service (
syncserver
orextensions_server
). - Backend Container Status: Is the
syncserver
(orextensions_server
if accessing/ext
) container running (docker compose ps
)? - Backend Container Logs: Check the logs of the backend container (
docker compose logs syncserver
) for errors during startup or request processing. - NPM Configuration: In NPM's Proxy Host settings:
- Is the "Forward Hostname / IP" correct (should be the service name, e.g.,
syncserver
)? - Is the "Forward Port" correct (e.g.,
3000
forsyncserver
,80
forextensions_server
)?
- Is the "Forward Hostname / IP" correct (should be the service name, e.g.,
- Docker Network: Are NPM and the backend service on the same Docker network defined in
docker-compose.yml
? Usedocker network inspect <network_name>
to verify.
- This usually means the reverse proxy (NPM) successfully received the request but could not connect to the backend service (
- Getting 500 Internal Server Error
- This indicates an error occurred within the backend application (
syncserver
) while processing the request. - Check
syncserver
Logs:docker compose logs syncserver
is essential here. Look for detailed error messages or stack traces. The error might be related to database issues, configuration problems, or bugs in the Standard Notes server code.
- This indicates an error occurred within the backend application (
- Notes Not Syncing Between Clients
- Server Connection: Ensure all clients are configured to point to the correct self-hosted server URL (
https://notes.yourdomain.com
) in Advanced Options. - Network Issues: Check for intermittent network problems on the client or server side.
- Server Logs: Check
syncserver
logs for any errors related to/items/sync
requests. - Client Logs (If Available): Some Standard Notes clients might have developer consoles or logging options that could provide clues.
- Resource Limits: Is the server under heavy load (CPU/RAM)?
- WebSockets: Ensure Websockets support is enabled in your NPM Proxy Host configuration for
notes.yourdomain.com
. While sync can work without it, Websockets improve real-time performance.
- Server Connection: Ensure all clients are configured to point to the correct self-hosted server URL (
- Extensions Not Loading / Extensions List Empty
- Client Configuration: Double-check the "Custom Extensions Repository" URL in the client's Advanced Options. It should point to the
index.json
file served via your reverse proxy (e.g.,https://notes.yourdomain.com/ext/index.json
). extensions_server
Container: Is theextensions_server
(Nginx) container running? Check its logs (docker compose logs extensions_server
). Are there errors about file permissions or configuration?- NPM Location Configuration: In NPM, check the "Location"
/ext
(or your chosen path) defined for yournotes.yourdomain.com
proxy host. Ensure it forwards correctly to theextensions_server
service on port 80. - File Paths/Permissions: Verify the
extensions-repo
directory was cloned correctly and theindex.json
file exists at the root. Ensure the Nginx container has read access (the:ro
flag in the volume mount helps prevent accidental changes but shouldn't block reads). - CORS Issues: Check the browser's developer console (usually F12) on the Standard Notes client page for Cross-Origin Resource Sharing (CORS) errors when it tries to load extensions. The
add_header Access-Control-Allow-Origin '*';
line in thenginx-extensions.conf
is intended to prevent this, but ensure it's correctly applied.
- Client Configuration: Double-check the "Custom Extensions Repository" URL in the client's Advanced Options. It should point to the
- Container Crashing or Restarting (
docker compose ps
shows restarting)- Check Logs Immediately:
docker compose logs <container_name>
(without-f
). Look at the very last messages before it crashed. This often contains the fatal error. - Resource Exhaustion: Did the container run out of memory? Check
docker stats
or system monitoring tools around the time of the crash. You might need to increase the server's RAM or configure memory limits for the container indocker-compose.yml
(though often better to give Docker access to sufficient host RAM). - Configuration Errors: Check environment variables, mounted configuration files, and volume paths for typos or incorrect settings.
- Disk Space: Is the host server out of disk space? (
df -h
) - Data Corruption: In rare cases, corrupted data in a volume could cause crashes on startup. Try restoring from a backup.
- Check Logs Immediately:
- Password Reset Email Not Arriving
- Check
syncserver
Logs: Look for errors related to SMTP connection, authentication, or sending emails after triggering a password reset. - SMTP Credentials/Configuration: Double-check
EMAIL_HOST
,EMAIL_PORT
,EMAIL_HOST_USER
,EMAIL_HOST_PASSWORD
,EMAIL_FROM
,EMAIL_SECURE
in your.env
file. Ensure they match your provider's requirements exactly. - Firewall (Outbound): Does your server's firewall allow outgoing connections on the SMTP port (e.g., 587 or 465)? (UFW default
allow outgoing
usually covers this, but check if you have custom rules). - Email Provider Issues: Check your SMTP provider's dashboard for sending logs or blocks. Are you hitting sending limits? Is your account active? Did they block the attempt?
- Spam Folder: Check the recipient's spam/junk folder.
- Recipient Server Issues: The receiving email server might be rejecting the email.
- Check
Workshop Debugging a Failing Connection
Goal: Systematically diagnose why a Standard Notes client cannot connect to the self-hosted server URL (https://notes.yourdomain.com
), simulating a common failure scenario.
Scenario: Your Standard Notes client suddenly shows "Cannot connect to server" or similar errors when trying to sync or log in to https://notes.yourdomain.com
.
Steps:
-
Verify Client Configuration:
- Action: Open Standard Notes client -> Advanced Options.
- Check: Confirm the "Sync Server" URL is exactly
https://notes.yourdomain.com
(no typos, correct HTTPS).
-
Check Basic Network Reachability (Client Side):
- Action: Open a terminal or command prompt on your client machine.
- Command:
ping notes.yourdomain.com
- Interpretation:
- Successful Ping: DNS is likely resolving correctly, and there's basic network connectivity. Move to Step 3.
- Unknown Host / Cannot Resolve: DNS issue. Check your DNS records at the registrar. Wait for propagation. Check your client device's network settings/DNS servers.
- Request Timeout: DNS might be resolving, but the server isn't responding to pings (ICMP might be blocked by a firewall), or there's a network routing issue. Proceed to Step 3, but keep firewall/routing in mind.
-
Check HTTPS Endpoint (Client Side):
- Action: Open a terminal/command prompt on your client machine.
- Command:
curl -I https://notes.yourdomain.com
(The-I
flag fetches headers only). - Interpretation:
- HTTP/2 200 OK (or similar 2xx/3xx): Server is responding correctly over HTTPS at the domain root. The issue might be specific to the sync API path or client state. Check server logs (Step 5).
- HTTP/1.1 404 Not Found (or similar 4xx): Still indicates the server is responding over HTTPS, just not for the root path
/
. This is often normal for the SN server root. Check server logs (Step 5). - Connection Refused / Timeout: The server is not accepting connections on port 443. Go to Step 4 (Server Firewall/NPM).
- SSL Certificate Error: Problems with the SSL certificate (expired, wrong domain, untrusted). Check NPM configuration (Step 4) and ensure certs are renewing.
- 5xx Server Error (e.g., 500, 502, 503, 504): The reverse proxy or backend server encountered an error. Go to Step 5 (Server Logs).
-
Check Server Firewall and Reverse Proxy (Server Side):
- Action: SSH into your server.
- Command (Firewall):
sudo ufw status
- Check: Ensure ports 80 (
http
) and 443 (https
) showALLOW IN
fromAnywhere
(or your specific IP range). If not, allow them:sudo ufw allow https
. - Command (NPM Status):
docker compose ps
(in your standardnotes directory) - Check: Is the
npm
container running (State: Up
)? Are ports80:80
and443:443
listed in thePORTS
column? - Command (NPM Logs):
docker compose logs npm
- Check: Look for recent errors related to your domain, SSL certificates, or failures connecting to the backend (
syncserver
). Fix any issues found (e.g., regenerate certs in NPM UI, correct forward host/port).
-
Check Standard Notes Server Logs (Server Side):
- Action: SSH into your server.
- Command:
docker compose logs syncserver
- Check: Look for errors around the time the client tried to connect. Are there database errors, configuration errors, or errors processing requests? Address any specific errors found.
-
Check Docker Networking (Server Side):
- Action: SSH into your server.
- Command:
docker network inspect standardnotes_network
(or your network name) - Check: Verify that both the
npm
container and thesyncserver
container are listed under "Containers". - Command (Internal Ping Test):
docker exec -it standardnotes_syncserver ping npm
anddocker exec -it nginx_proxy_manager ping syncserver
(Use actual container names). - Interpretation: If pings fail, there's a Docker networking issue. Check
docker-compose.yml
network definitions. Ensure containers are attached to the correct network. Sometimes recreating the network and containers helps (docker compose down
,docker network rm standardnotes_network
,docker compose up -d
).
-
Check Server Resources (Server Side):
- Action: SSH into your server.
- Commands:
htop
,df -h
,free -h
,docker stats
- Check: Is CPU pegged at 100%? Is RAM/Swap full? Is the disk partition containing
/path/to/standardnotes/sn_data
or/var/lib/docker
full? Resource exhaustion can cause connection failures or instability. Free up resources or upgrade the server if necessary.
Outcome: By following these steps, you systematically checked potential failure points from the client-side DNS and connectivity, through the server's firewall and reverse proxy, down to the backend application logs and server resources. This methodical approach helps pinpoint the root cause of the connection failure, allowing you to apply the correct fix.
Conclusion
Embarking on the journey of self-hosting Standard Notes is a rewarding endeavor that places you firmly in control of your most valuable digital asset: your personal notes and knowledge base. Throughout this guide, we've navigated the process from fundamental concepts to advanced configurations, equipping you with the knowledge and practical skills needed to run your own secure and private note-syncing service.
We began by understanding the Standard Notes ecosystem, appreciating the roles of the client applications, the zero-knowledge Syncing Server, and the optional Extensions Server, all underpinned by robust end-to-end encryption. We then meticulously prepared a suitable Linux server environment, emphasizing the power and convenience of Docker and Docker Compose for streamlined deployment and management.
Our practical workshops led you through deploying the core Syncing Server, initially testing connectivity, and then crucially securing it with HTTPS using Nginx Proxy Manager and Let's Encrypt certificates – an essential step for protecting data in transit. We unlocked the full potential of Standard Notes by deploying the Extensions Server, granting access to powerful editors and themes hosted entirely on your infrastructure.
Recognizing that self-hosting comes with responsibility, we delved into critical operational aspects: implementing data persistence using Docker volumes and establishing a basic automated backup strategy with cron
and tar
. We explored server monitoring using tools like Uptime Kuma to ensure availability and performance, and discussed the importance of regular maintenance, including system and container updates. Finally, we addressed advanced configurations like enabling email password resets and provided a systematic troubleshooting methodology to tackle common issues.
By successfully setting up and managing your own Standard Notes instance, you've gained more than just a private note-taking app. You've practiced essential sysadmin skills, worked with containerization, managed web servers and reverse proxies, handled TLS/SSL certificates, and taken tangible steps towards greater digital sovereignty.
The path of self-hosting is one of continuous learning. We encourage you to delve deeper, explore alternative tools, refine your backup and monitoring strategies, and perhaps even contribute back to the open-source projects that make this possible. Your self-hosted Standard Notes instance is a testament to your ability to manage your own digital infrastructure, ensuring your thoughts and ideas remain truly yours, secure and accessible under your control.