SilverBullet
Container
Notes app for Markdown pages you can program, with two-way links, tasks, queries over everything you've written and Lua scripting for your own commands and widgets. Source: https://github.com/silverbulletmd/silverbullet
Image details
Source details
Configuration
TypeContainerlinuxghcr.io/silverbulletmd/silverbullet:latest-slim3300:3000/tcp/dataunless-stoppedTemplate by NotAFlightRisk
Report issueNotes
http://<your-host>:3300 and the setup wizard asks for an admin account, then your first space (at /notes by default). There's no setup code, so whoever gets there first owns the server - do it straight after deploying, and from the address you'll actually use, since the admin dashboard always sends you back there (change it under Admin --> Server). Over plain http on anything but localhost it runs online-only, so no offline use, local encryption or single sign-on. Put it behind an HTTPS proxy to get those back. Accounts, settings and every page (as plain Markdown files) live in the volume. This is the slim image, so if you want the Runtime API, which needs Chromium, use the latest tag instead. Docs: docs.silverbullet.mdStandalone Install
Select an install method, to see config/commands for deploying SilverBullet
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select SilverBullet, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
More install options in our documentation, or see silverbulletmd/silverbullet for app-specific guidance.
SilverBullet
SilverBullet is a Programmable, Private, Browser-based, Open Source, Self Hosted, Personal Knowledge Database — a fancy term for a "notes app on steroids".Yowza! That surely is a lot of adjectives to describe a browser-based Markdown editor programmable with Lua.
Let’s get more specific.
SilverBullet combines a clean live-preview editor with wiki-style linking, a built-in database and query language, and a fully integrated Space Lua scripting environment, turning your notes into a programmable system that grows with you.
In SilverBullet you keep your content as a collection of Markdown Pages (called a Space). You navigate your space using the Page Picker like a traditional notes app, or through Links like a wiki (except they are bi-directional).
If you are the writer type, you’ll appreciate SilverBullet as a clean Markdown editor with Live Preview. If you have more of an outliner personality, SilverBullet has Outlining tools for you. Productivity freak? Have a look at Tasks. More of a database person? You will appreciate Objects and Queries (SLIQ).
And if you are comfortable programming a little bit — now we’re really talking. You will love dynamically generating content with Space Lua (SilverBullet’s Lua dialect), or to use it to create custom Commands, Page Templates or Widgets.
You were told there’s no such thing as a silver bullet. You were told wrong.
Much more detail can be found on silverbullet.md
Installing SilverBullet
Check out the instructions.Developing SilverBullet
SilverBullet's frontend ("client") is written in TypeScript and built on top of the excellent CodeMirror 6 editor component. Additional UI is built using Preact. ESBuild is used to build the frontend.The server backend is written in Rust (a Cargo workspace). It serves the pre-built client bundle, the file/space HTTP API, authentication, and a headless-Chrome runtime for server-side Lua.
Code structure
bin/silverbullet/: The standalonesilverbulletserver binary (Rust) (this is the one you generally run)bin/sb/: ThesbCLI client (Rust)bin/plug-compile.ts: the plug compilerclient/: The SilverBullet client (browser, TypeScript)server/: The SilverBullet server library (Rust): HTTP router, handlers, auth, runtime seamserver-common/: Shared Rust crate (space primitives, shared types)server-runtime-chrome/: Headless-Chrome runtime backend (Rust)plugs: Set of built-in plugs (TypeScript) that are distributed with SilverBulletlibraries: A set of libraries (space scripts, page templates, slash templates) distributed with SilverBullet (Space Lua)plug-api/: Useful APIs for use in plugs and published to NPM
lib/: Useful libraries to be used in plugs
syscalls/: TypeScript wrappers around syscalls
types/: Various (client) types that can be references from plugsscripts/: Useful scriptsdocs/: documentation (published as docs.silverbullet.md and bundled as Desktop's Help)
Requirements
- Node.js 24+ and npm 10+: Used to build the frontend (client) and plugs
- Rust (stable, via
rustup): Used to build the server
The project includes
.nvmrc and .node-version files. If you use nvm or another Node version manager, it will automatically use the correct Node.js version:nvm use # If using nvmFirst, install dependencies:
make setupTo create a (release) build:
makeThis will produce
silverbullet and cli binaries in ./target/release/, run ./target/release/silverbullet to run the server.Development workflow
SilverBullet has two halves you rebuild independently:- The client (TypeScript:
client/) is built by ESBuild intoclient_bundle/, which the server then serves. - The server (Rust:
server/,server-common/,server-runtime-chrome/,bin/silverbullet/) is a compiled binary.
Run the server in development with
cargo run. A debug build serves the client bundle live from client_bundle/ on disk (a release build embeds it instead):npm run build
cargo run <PATH-TO-YOUR-DATA-FOLDER>When you change only the client (TypeScript in
client/): you do not need to restart the server. Rebuild just the client bundle and reload the page in your browser — the debug server serves the freshly-built bundle straight from disk:npm run buildTo build a self-contained release binary (with the client bundle embedded), and run it:
make
./target/release/silverbullet <PATH-TO-YOUR-DATA-FOLDER>Useful development tasks
# Clean all generated files
make clean
# Typecheck and lint all code
make check
# Format all code
make fmt
# Run all tests
make test
# Run benchmarks
make benchBuild a docker container
Note, you do not need Node.js nor Rust locally installed for this to work:docker build -t silverbullet .To run:
docker run -p 3000:3000 -v <PATH-TO-YOUR-DATA-FOLDER>:/data silverbulletServe SilverBullet on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.
Proxying silverbullet.example.com to http://silverbullet:3000
Add this to your Caddyfile
silverbullet.example.com {
reverse_proxy http://silverbullet:3000
}Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs silverbullet - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:3300 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :3300 - Stop the other service, or pick a different host port. In
3300:3000only the left number is yours to change, the right one belongs to the app.
Running but the page won't load
The container is up but nothing appears in your browser.
- Use your server's real IP:
http://your-server-ip:3300. The 0.0.0.0 link Portainer shows isn't a real address. - Give it a minute after first deploy, silverbullet can take a while to initialise.
- Make sure your firewall allows the port, e.g.
sudo ufw allow 3300
Image won't pull
Test the pull directly on the host: docker pull ghcr.io/silverbulletmd/silverbullet:latest-slim
- "manifest unknown" means the tag no longer exists.
- "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64, arm/v7 - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect silverbullet --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue on silverbulletmd/silverbullet
- Template not working: Open an issue on NotAFlightRisk/portainer-templates-extras
- This website not working: Open an issue on lissy93/portainer-templates
A single container
SilverBullet runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.
The app image
An image is the app packed up ready to go, everything SilverBullet needs bundled into one download. This template pulls ghcr.io/silverbulletmd/silverbullet:latest-slim, which Docker fetches once (about 38 MB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. SilverBullet's comes from the GitHub Container Registry, published by silverbulletmd.
Version tags
The bit after the colon in the image name is the version tag. This one pins latest-slim, so every redeploy gives you that exact build until you bump it yourself.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, arm/v7, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 3300:3000 means it's reachable on port 3300 of your server, where the left number is yours to change and the right one belongs to the app. Once it's running, open http://your-server-ip:3300 in a browser. It opens:
3300:3000, likely the web interface
Volumes
A volume is where SilverBullet keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:
/dataas a volume Docker manages for you
Restart policy
The restart policy here is unless-stopped, so Docker restarts SilverBullet after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Networking
Nothing custom is set, so SilverBullet sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container silverbullet. That's what you'll spot in the containers list and use in commands like docker logs silverbullet.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Open source license
SilverBullet is open source, released under the MIT license. In plain terms the code is out in the open, so you're free to run it and change it to fit what you need.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set SilverBullet up. Add the template list to Portainer once, then deploying SilverBullet is a click rather than a wall of config.