mirror of
https://github.com/jbowdre/runtimeterror.git
synced 2024-11-21 22:42:19 +00:00
new post: silverbullet-self-hosted-knowledge-management
This commit is contained in:
parent
bae5c63a70
commit
e6180a1815
1 changed files with 40 additions and 27 deletions
|
@ -1,9 +1,8 @@
|
||||||
---
|
---
|
||||||
title: "SilverBullet: A Brilliant Self-Hosted Knowledge Management Web App"
|
title: "SilverBullet: Self-Hosted Knowledge Management Web App"
|
||||||
date: 2024-08-12
|
date: "2024-08-22T02:56:12Z"
|
||||||
# lastmod: 2024-08-12
|
# lastmod: 2024-08-12
|
||||||
draft: true
|
description: "Deploying SilverBullet with Docker Compose, and accessing it from anywhere with Tailscale and Cloudflare Tunnel."
|
||||||
description: "This is a new post about..."
|
|
||||||
featured: false
|
featured: false
|
||||||
toc: true
|
toc: true
|
||||||
reply: true
|
reply: true
|
||||||
|
@ -12,13 +11,12 @@ tags:
|
||||||
- cloudflare
|
- cloudflare
|
||||||
- containers
|
- containers
|
||||||
- docker
|
- docker
|
||||||
- javascript
|
|
||||||
- linux
|
- linux
|
||||||
- selfhosting
|
- selfhosting
|
||||||
- tailscale
|
- tailscale
|
||||||
---
|
---
|
||||||
|
|
||||||
A few days ago I [posted on my other blog](https://srsbsns.lol/is-silverbullet-the-note-keeping-silver-bullet/) about trying out [SilverBullet](https://silverbullet.md), an open-source self-hosted web-based note-keeping app. SilverBullet has continued to impress me as I use it and learn more about its [features](https://silverbullet.md/SilverBullet@1992). It really fits my multi-device use case much better than Obsidian ever did (even with its paid sync plugin).
|
I [recently posted on my other blog](https://srsbsns.lol/is-silverbullet-the-note-keeping-silver-bullet/) about trying out [SilverBullet](https://silverbullet.md), an open-source self-hosted web-based note-keeping app. SilverBullet has continued to impress me as I use it and learn more about its [features](https://silverbullet.md/SilverBullet@1992). It really fits my multi-device use case much better than Obsidian ever did (even with its paid sync plugin).
|
||||||
|
|
||||||
In that post, I shared a brief overview of how I set up SilverBullet:
|
In that post, I shared a brief overview of how I set up SilverBullet:
|
||||||
|
|
||||||
|
@ -40,11 +38,12 @@ sudo chown john:docker /opt/silverbullet # [tl! .cmd:1]
|
||||||
cd /opt/silverbullet
|
cd /opt/silverbullet
|
||||||
```
|
```
|
||||||
|
|
||||||
### SilverBullet
|
### SilverBullet Setup
|
||||||
The documentation offers easy-to-follow guidance on [installing SilverBullet with Docker Compose](https://silverbullet.md/Install/Docker), and that makes for a pretty good starting point. The only change I make here is setting the `SB_USER` variable from an environment variable instead of directly in the YAML:
|
The documentation offers easy-to-follow guidance on [installing SilverBullet with Docker Compose](https://silverbullet.md/Install/Docker), and that makes for a pretty good starting point. The only change I make here is setting the `SB_USER` variable from an environment variable instead of directly in the YAML:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# torchlight! {"lineNumbers":true}
|
# torchlight! {"lineNumbers":true}
|
||||||
|
# docker-compose.yml
|
||||||
services:
|
services:
|
||||||
silverbullet:
|
silverbullet:
|
||||||
image: zefhemel/silverbullet
|
image: zefhemel/silverbullet
|
||||||
|
@ -64,17 +63,22 @@ services:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
I used a password manager to generate a random password *and username*, and I store those in a `.env` file alongside the Docker Compose configuration. For example:
|
I used a password manager to generate a random password *and username*, and I stored those in a `.env` file alongside the Docker Compose configuration; I'll need those credentials to log in to each SilverBullet session. For example:
|
||||||
|
|
||||||
```text
|
```shell
|
||||||
|
# .env
|
||||||
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tailscale
|
That's all that's needed for running SilverBullet locally, and I *could* go ahead and `docker compose up -d` to get it running. But I really want to be able to access my notes from other systems too, so let's move on to enabling remote access right away.
|
||||||
That's all that's really needed for running SilverBullet locally, but I also want to be able to access the application from any device connected to my Tailscale tailnet. So I add in a [Tailscale sidecar](/tailscale-serve-docker-compose-sidecar/#compose-configuration), and update the `silverbullet` service to share Tailscale's network:
|
|
||||||
|
### Remote Access
|
||||||
|
#### Tailscale
|
||||||
|
It's no secret that I'm a [big fan of Tailscale](/secure-networking-made-simple-with-tailscale/) so I use Tailscale Serve to enable secure remote access through my tailnet. I just need to add in a [Tailscale sidecar](/tailscale-serve-docker-compose-sidecar/#compose-configuration) and update the `silverbullet` service to share Tailscale's network:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# torchlight! {"lineNumbers":true}
|
# torchlight! {"lineNumbers":true}
|
||||||
|
# docker-compose.yml
|
||||||
services:
|
services:
|
||||||
tailscale: # [tl! ++:12 **:12]
|
tailscale: # [tl! ++:12 **:12]
|
||||||
image: tailscale/tailscale:latest
|
image: tailscale/tailscale:latest
|
||||||
|
@ -102,18 +106,22 @@ services:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
network_mode: service:tailscale # [tl! ++ **]
|
network_mode: service:tailscale # [tl! ++ **]
|
||||||
|
|
||||||
watchtower:
|
watchtower: # [tl! collapse:4]
|
||||||
image: containrrr/watchtower
|
image: containrrr/watchtower
|
||||||
container_name: silverbullet-watchtower
|
container_name: silverbullet-watchtower
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
That of course means adding a few more items to the `.env` file: a [pre-authentication key](https://tailscale.com/kb/1085/auth-keys), the hostname to use for the application's presence on my tailnet, and the `--ssh` extra argument to enable SSH access to the container (not strictly necessary, but can be handy for troubleshooting):
|
That of course means adding a few more items to the `.env` file:
|
||||||
|
- a [pre-authentication key](https://tailscale.com/kb/1085/auth-keys),
|
||||||
|
- the hostname to use for the application's presence on my tailnet,
|
||||||
|
- and the `--ssh` extra argument to enable SSH access to the container (not strictly necessary, but can be handy for troubleshooting).
|
||||||
|
|
||||||
```text
|
```shell
|
||||||
|
# .env
|
||||||
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
||||||
TS_AUTHKEY=tskey-auth-[...] [tl! ++:2 **:2]
|
TS_AUTHKEY=tskey-auth-[...] # [tl! ++:2 **:2]
|
||||||
TS_HOSTNAME=silverbullet
|
TS_HOSTNAME=silverbullet
|
||||||
TS_EXTRA_ARGS=--ssh
|
TS_EXTRA_ARGS=--ssh
|
||||||
```
|
```
|
||||||
|
@ -122,6 +130,7 @@ And I need to create a `serve-config.json` file to configure [Tailscale Serve](/
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// torchlight! {"lineNumbers":true}
|
// torchlight! {"lineNumbers":true}
|
||||||
|
// serve-config.json
|
||||||
{
|
{
|
||||||
"TCP": {
|
"TCP": {
|
||||||
"443": {
|
"443": {
|
||||||
|
@ -140,17 +149,18 @@ And I need to create a `serve-config.json` file to configure [Tailscale Serve](/
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cloudflare Tunnel
|
#### Cloudflare Tunnel
|
||||||
But what if I want to consult my notes from outside of my tailnet? Sure, I *could* use [Tailscale Funnel](/tailscale-ssh-serve-funnel/#tailscale-funnel) to publish the SilverBullet service on the internet, but (1) funnel would require me to use a URL like `https://silverbullet.tailnet-name.ts.net` instead of simply `https://silverbullet.example.com` and (2) I'm still a little wary of putting a login page on the public web.
|
But what if I want to consult my notes from *outside* of my tailnet? Sure, I *could* use [Tailscale Funnel](/tailscale-ssh-serve-funnel/#tailscale-funnel) to publish the SilverBullet service on the internet, but (1) funnel would require me to use a URL like `https://silverbullet.tailnet-name.ts.net` instead of simply `https://silverbullet.example.com` and (2) I've seen enough traffic logs to not want to expose a login page directly to the public internet if I can avoid it.
|
||||||
|
|
||||||
[Cloudflare Tunnel](/publish-services-cloudflare-tunnel/) is able to address those concerns without a lot of extra work. I can set up a tunnel at `silverbullet.example.com` and use [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/) to put an additional challenge in front of the login page.
|
[Cloudflare Tunnel](/publish-services-cloudflare-tunnel/) is able to address those concerns without a lot of extra work. I can set up a tunnel at `silverbullet.example.com` and use [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/) to put an additional challenge in front of the login page.
|
||||||
|
|
||||||
I just need to add a `cloudflared` container to my stack:
|
I just have to add a `cloudflared` container to my stack:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# torchlight! {"lineNumbers":true}
|
# torchlight! {"lineNumbers":true}
|
||||||
|
# docker-compose.yml
|
||||||
services:
|
services:
|
||||||
tailscale:
|
tailscale: # [tl! collapse:12]
|
||||||
image: tailscale/tailscale:latest
|
image: tailscale/tailscale:latest
|
||||||
container_name: silverbullet-tailscale
|
container_name: silverbullet-tailscale
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -185,26 +195,27 @@ services:
|
||||||
- ./space:/space
|
- ./space:/space
|
||||||
network_mode: service:tailscale
|
network_mode: service:tailscale
|
||||||
|
|
||||||
watchtower:
|
watchtower: # [tl! collapse:4]
|
||||||
image: containrrr/watchtower
|
image: containrrr/watchtower
|
||||||
container_name: silverbullet-watchtower
|
container_name: silverbullet-watchtower
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
To get the required `$CLOUDFLARED_TOKEN`, I have to [create a new `cloudflared` tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/) in the Cloudflare dashboard, and then I just add the generated value to my `.env` file:
|
To get the required `$CLOUDFLARED_TOKEN`, I [create a new `cloudflared` tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/) in the Cloudflare dashboard and add the generated token value to my `.env` file:
|
||||||
|
|
||||||
```text
|
```shell
|
||||||
|
# .env
|
||||||
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
SB_CREDS='alldiaryriver:XCTpmddGc3Ga4DkUr7DnPBYzt1b'
|
||||||
TS_AUTHKEY=tskey-auth-[...]
|
TS_AUTHKEY=tskey-auth-[...]
|
||||||
TS_HOSTNAME=silverbullet
|
TS_HOSTNAME=silverbullet
|
||||||
TS_EXTRA_ARGS=--ssh
|
TS_EXTRA_ARGS=--ssh
|
||||||
CLOUDFLARED_TOKEN=eyJhIjo[...]BNSJ9 [tl! ++ **]
|
CLOUDFLARED_TOKEN=eyJhIjo[...]BNSJ9 # [tl! ++ **]
|
||||||
```
|
```
|
||||||
|
|
||||||
Back in the Cloudflare Tunnel setup flow, I select my desired public hostname (`silverbullet.example.com`) and then specify that the backend service is `http://localhost:3000`.
|
Back in the Cloudflare Tunnel setup flow, I select my desired public hostname (`silverbullet.example.com`) and then specify that the backend service is `http://localhost:3000`.
|
||||||
|
|
||||||
Now I'm ready to start up my containers:
|
Now I'm finally ready to start up my containers:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker compose up -d # [tl! .cmd .nocopy:1,5]
|
docker compose up -d # [tl! .cmd .nocopy:1,5]
|
||||||
|
@ -216,8 +227,8 @@ docker compose up -d # [tl! .cmd .nocopy:1,5]
|
||||||
✔ Container silverbullet-cloudflared Started
|
✔ Container silverbullet-cloudflared Started
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cloudflare Access
|
#### Cloudflare Access
|
||||||
The finishing touch will be configuring a bit of extra protection in front of the public-facing login page, and Cloudflare Access makes that very easy. I'll just used the wizard to [add a new web application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/) through the Cloudflare Zero Trust dashboard.
|
The finishing touch will be configuring a bit of extra protection in front of the public-facing login page, and Cloudflare Access makes that very easy. I'll just use the wizard to [add a new web application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/) through the Cloudflare Zero Trust dashboard.
|
||||||
|
|
||||||
The first part of that workflow asks "What type of application do you want to add?". I select **Self-hosted**.
|
The first part of that workflow asks "What type of application do you want to add?". I select **Self-hosted**.
|
||||||
|
|
||||||
|
@ -228,4 +239,6 @@ I'm then asked to Add Policies, and I have to start by giving a name for my poli
|
||||||
And then I just click through the rest of the defaults.
|
And then I just click through the rest of the defaults.
|
||||||
|
|
||||||
### Recap
|
### Recap
|
||||||
So now I have deployed SilverBullet in Docker Compose on a server in my homelab. I can access it from any device on my tailnet at `https://silverbullet.tailnet-name.ts.net` (thanks to the magic of Tailscale Serve). And I can visit it on external devices at `https://silverbullet.example.com` (thanks to Cloudflare Tunnel),
|
So now I have SilverBullet running in Docker Compose on a server in my homelab. I can access it from any device on my tailnet at `https://silverbullet.tailnet-name.ts.net` (thanks to the magic of Tailscale Serve). I can also get to it from outside my tailnet at `https://silverbullet.example.com` (thanks to Cloudflare Tunnel), and but I'll use a one-time passcode sent to my approved email address before also authenticating through the SilverBullet login page (thanks to Cloudflare Access).
|
||||||
|
|
||||||
|
I think it's a pretty sweet setup that gives me full control and ownership of my notes and lets me read/write my notes from multiple devices without having to worry about synchronization.
|
Loading…
Reference in a new issue