Silverbullet is the easiest pkm program to self-host
One of the biggest problems about pkm solutions is syncing. As such, it’s usually one of the few things that you get charged for when going with something like Obsidian. Silverbullet is a simple way to self host a pkm setup. A single docker container does the interface, database, and syncing service. Throw in vim mode and the ability to easily customize themes, and we have a really cool project. You can access the program from any web browser, or on mobile through the progressive web application.

Installation
Installation is really straight forward with a compose.yml file. The official docs mention watchtower, which you can add if you want auto updates, however I don’t think it is necessary.
services:
silverbullet:
image: ghcr.io/silverbulletmd/silverbullet
restart: unless-stopped
environment:
- SB_USER=admin:admin
volumes:
- ./space:/space
ports:
- 3000:3000 #If you don't need to expose the port to the network directly, use 127.0.0.1:3000:3000
You could also use docker secrets if you don’t want to store the password in the compose.yml file. Whatever you do, please don’t leave it as admin:admin if you are having this exposed to the internet.
You can also spin this up in pikapods if you don’t want to host it yourself.
It also makes sense to put the service behind a reverse proxy. A simple one-liner in something like caddy should suffice:
wiki.example.com {
reverse_proxy localhost:3000
}
Usage
The user interface is really simplistic. You have three actions to pick from at any given time.
- Go home
- Open a page
- Run a command
The first two do what they say on the tin. The last one gives some flexibility.
Useful commands
All commands will be shown in the dropdown when you press the button, so I won’t document them here. A few useful ones that I found are Toggle Vim Mode and Export Page (you can then export the page as clean markdown to the clipboard). The vim mode works really well out of the box, however you can customize even further with some configuration files if you wish. Note that the command will only take effect on the client you are running on, meaning you don’t have to worry about vim mode on mobile (thank goodness!)
Styling
To create your own theme, you can load custom css into a space-style codeblock on the CONFIG page. You can check out catppuccin themes here[1], other themes can be found around the web.
Scripting
Another really cool thing about Silverbullet is that you can use lua (space-lua to be specific) to get creative. For example, on the homepage you could create a list of the 5 most recently updated posts with
${template.each(query[[
from index.tag "page"
order by _.lastModified desc
limit 5
]], templates.pageItem)}
Who might want to use this
Silverbullet might work for you if:
- You are comfortable with self-hosting (or paying a small amount to pikapods each month)
- You want to edit with markdown in your browser
- You are okay without a dedicated app for mobile devices (Silverbullet works as a PWA)
- Would benefit from being able to write scripts directly in lua
Silverbullet may not be for you if:
- You are looking for advanced features, such as Obsidian’s bases. Silverbullet offers some extensibility with lua scripting, and Plugs, but the community is smaller and overall Silverbullet is a less mature product.
- You want end-to-end encryption on syncing. If someone can access the server running Silverbullet, they will be able to view the data.
Backing up data
All of the data produced by silverbullet lives in a single volume, and is stored in plain markdown files. To perform a backup you can just copy the contents of the folder, or setup automatic backups through something like borgbase.
The location of the files are defined in this line of the compose. In the example, they will live in a directory called space within the same directory of the compose.yml file.
volumes:
- ./space:/space
The repository name is just silverbullet in preparation for moving the repo into the catppuccin organization. If the port is accepted, I’ll update the link here to point to that repo instead, and archive the current one. ↩︎