Silly Tavern on Single Board Computer (SBC)
Why Use Silly Tavern on an SBC?
Running Silly Tavern on a Single Board Computer (SBC) offers several advantages:
- Intranet Access: Easily access your models and cards from any web browser within your local network.
- Centralized Management: Consolidate all cards and API endpoints in one location for access from multiple devices.
- Learning Opportunity: Gain hands-on experience with lower-level networking concepts (focused here on Ubuntu).
- Compact and Efficient: A headless setup (no monitor or keyboard) keeps the system compact—about the size of a deck of cards—and uses only ~15W of power. It can travel with you, running off a small wall adapter or even a USB battery pack.
- Lauchpad for internet access: Good starting point to set up a machine for internet access that's not your main computer.
TOC
What You Need / Hardware Requirements:
- SBC with minimum 1GB RAM and 16GB storage, running a modern version of Ubuntu.
- More on minimum requirements below: https://rentry.org/SillyTavernOnSBC#more-about-minimum-hardware-requirements
- Guide is based on my setup of an Orange Pi 3B and an Orange Pi Zero 2W, both running Ubuntu OS
- Access to intranet via ethernet or Wi-Fi. This guide assumes you're using WiFi.
- Monitor, keyboard, and mouse for initial setup. These can be set aside after going headless.
Other Essentials:
- Basic Linux knowledge and a willingness to learn more.
- ChatGPT to troubleshoot and answer questions along the way.
How to Set It Up
This is a rough guide outlining the process I followed, including key steps and challenges encountered. It's designed with minimal guidance but highlights areas where I got stuck. If additional help is needed, querying ChatGPT for specific issues is effective—especially as it gains context about your setup. Or perhaps below will be enough.
- Install Ubuntu on your SBC. For Orange Pi the Ubuntu image should be running XFCE as a Xwin GUI manager. Follow instruction for your SBC if it's not
- Below assumes you're installing Ubuntu Desktop version. You could use Server, but Desktop's a bit more user friendly, plus allows you to test ST with SBC installed browser
- Lower power machines e.g. Zero 2W will come to a crawl trying to run a browser + ST (I had to SSH in and shutdown remotely setting one up). For those systems, you may want to skip ahead and kill the Desktop early, doing install from terminal window or SSH, or use Server version of Ubuntu from the beginning
- Get machine set up with monitor, keyboard, mouse. We'll use the windowed display to do initial setup, then turn it off later.
- Get SBC hooked up to the WiFI network. Use the windowed tools...
- Run
sudo orangepi-config
orsudo raspberrypi-config
now and enable SSH if you're using one of these machines - Update / upgrade the OS to get that out of the way now:
sudo apt-get update
,sudo apt-get upgrade
- Go get a coffee; it takes awhile... 30-60 minutes
- Install Git on your machine:
sudo apt-get install git
- Install SillyTavern on your machine
git clone https://github.com/SillyTavern/SillyTavern -b release
- Ensure SillyTavern works and can be seen on the local browser. Run
bash start.sh
, you may need to restart it a couple times as it installs libraries and sets up.- Note that it may be dog slow once the browser launches... browsers are resource intensive. No worries because we'll only use it for testing.
- I ran into whole issue here around Node.js not being up to date. As of writing, ST require v18 or better. ST errors on this aren't obv (at least weren't to me). Some basics to diagnose failures, as ST will attempt to install some of the below and can fail at it:
- Check Node.js version
node -v
- List all installed Node.js versions
nvm list
- If nvm is missing, install it, then have nvm install v18 of nodejs. ST should then install npm on startup.
- Install Node.js v18
nvm install 18
,nvm use 18
- Uninstall Node.js v16 (for example) when they refuse to let go
nvm uninstall 16
- Check Node.js version
- Might be better off uninstalling the whole thing and starting over from scratch
- Getting NodeJS, NVM, NPM working is critical and bounds in what hardware you can use. There are alt repos for non-standard hardware, but TLDR if it runs a modern version of Ubuntu you should be OK.
- Follow the ST guide to get ST to start talking on your network (https://docs.sillytavern.app/usage/remoteconnections/)
- Edit ST config file,
~/SillyTavern/default/config.yaml
and setautorun: false
to stop ST from opening up a browser on startup- There's an additional config file
~/SillyTavern/config.yaml
which also has autorun setting. Seems like it dominates the other... I guess change both of them...
- There's an additional config file
- Test access to the SBC using port 8000 on one of your computers
- Get SSH set up on your daily driver (Windows: PuTTY, for example)
- Hook into your SBC via SSH with your daily driver
- This turned into another whole thing. The TLDR is if its not working, uninstall SSH and reinstall it
sudo apt-get remove --purge openssh-server
sudo apt-get install openssh-server
- Default SSH is via port 22
- This turned into another whole thing. The TLDR is if its not working, uninstall SSH and reinstall it
- So now ST will run in an open Xterm window. We want it to run in the background, and restart every time the machine powers up
- We will us
pm2
to do thisnpm install -g pm2
- From ST directory:
pm2 start start.sh
pm2 save
to save this setup,pm2 startup
, to keep it running after reboot (follow the instructions)
- We will us
- At this point: ST is running in background, we can access it via WiFi, and we can SSH into machine. So, we can (and should) shut off the Xwin GUI. Below is for XFCE, steps will be similar for other OS
sudo systemctl set-default multi-user.target
Start in command linesudo reboot
Restart, you should get a simple CLI now- To get Xwin GUI back:
sudo systemctl set-default graphical.target
(permanent)sudo systemctl start lightdm
(one time) - You can also now uninstall the Xwin GUI package to save space. I didn't bother with this step.
- You can monitor ST in SSH via
pm2 log
, which will allow you to see the entire context being sent via API - Unplug the keyboard, mouse, monitor from SBC, and put SBC in its new home
- Everything should be running now; last bit are some security issues
- At bare minimum, change the default uname pwd:
sudo passwd orangepi
for example - Rest of discussion on security is here, since it's a lengthy topic of its own: https://rentry.org/SillyTavernOnSBC#more-about-minimum-hardware-requirements
- At bare minimum, change the default uname pwd:
Where to Place the SBC with ST
Once the setup is complete, you can position the SBC anywhere with power and access to Wi-Fi or your intranet. Remote management is possible via SSH. Most convenient place for it may be hooked with ethernet cable and sitting next to your router.
Remote Maintenance Items / Other Things for Later
- Use
git pull
from the ST directory to update installation - To monitor the context (useful if you're writing cards and can't figure out why the LLM is responding differently than expected)
- SSH into SBC
pm2 log
will show context ala the terminal window
- Device falling off network: I found SBC would fall off the WiFi network after 24 hours or so. This was solved by doing a few things, together:
- Add network reconnect script: See below for a chunk of code to have the SBC to monitor and re-start network if WiFi drops: https://rentry.org/SillyTavernOnSBC#code-network-monitor-and-reconnect
- Ensure power settings aren't shutting it down: Check power settings; for Ubuntu, look at
/etc/system/sleep.conf
and add (or uncomment) linesSuspendState=
andHibernateState=
to disable those states. - Prevent WiFi connection losing its router reservation after 24 hours: Configure your router to set a consisten IP and make the reservation permanent, which prevents router from knocking your SBC off the network after ~24 hours or so
- Log in to the Router Admin Interface, look for LAN Settings, DHCP Server, or Address Reservation.
- Add your device, note that this will permanently assign an IP address
- Address Reservation is done in lieu of DHCP Client list addition / bindingm which on my router is a different thing
- Daily reboot: I found with above it was still falling off the network, and required daily reboot... so I automated that too. Daily reboot (in this case, 3AM system time):
- Edit the cronjob
crontab -e
- Add
0 3 * * * /sbin/shutdown -r now
- Edit the cronjob
- If you need to add new WiFi from command line, use nmcli
-nmcli connection show
,nmcli device wifi list
to see what connections SBC has, and what exist around it
-nmcli device wifi connect "<SSID>" password "<password>"
to add new connection - Watch the heat on the processors. If they are running hot, take a look at the CPU settings and change it to either Powersave or Ondemand power schema, while lowering the highest CPU clock setting. The OPi Zero 2W ran hot until I did this, moving max from 1.3 GHz to 1.0 GHz (69C to 56C).
- Easiest is using
sudo orangepi-config
orraspberrypi-config
and change it under system settings - From command line,
sudo bash -c 'echo 1008000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq'
will reduce max CPU clock to 1.0Ghz (for example). Or some such - You could also add fans and fin coolers...
- Easiest is using
Optional Configurations (Not Implemented)
- Internet Access: Set up ST for access from anywhere over the internet. The developers advise caution as this exposes your network to the outsise, and provide instructions on how to setup and manage it.
- Peer-to-Peer Setup: Configure ST for peer-to-peer operation. This may be useful when traveling, though running it locally might suffice.
- TTS/STT* At some point I want to try making the device inputs based on ambient audio, which would make the device truly independent.
Other Resources
Links
- My pointers guide on character card creation: https://rentry.org/NG_CharCard
- My intro guide to lorebooks in Silly Tavern: https://rentry.org/SillyT_Lorebook
- Entire collection of Botmaker guides: https://rentry.org/meta_botmaking_list
- Detailed guide on Bot Building, lots of discussion on Formats and their history: https://rentry.org/MothsBotMakingStuff
Code: Network Monitor and Reconnect
Simple network monitoring and reconnect script that periodically checks the connection and attempts to reconnect if disconnected.
1. Use a Shell Script to Monitor the Connection
Create a script that pings a reliable external server (like Google's DNS server 8.8.8.8
) and reconnects if there's no response.
Script:
2. Save and Run the Script
- Save the script as
network-monitor.sh
. - Make it executable:
chmod +x network-monitor.sh
- Run it in the background:
nohup ./network-monitor.sh &
3. Automate at Startup
To ensure the script runs on boot:
- Edit the crontab:
crontab -e
- Add the following line (replace /path/to/ with correct path info:
@reboot /path/to/network-monitor.sh
Code: Miku ASCII at SSH signin
The ASCII art and system information you see upon logging into your headless Ubuntu device is typically generated by a login banner. It can be changed by editing the MOTD (Message of the Day) script. For this machine, MOTD is in /etc/update-motd.d/
, specifically in this case, /etc/update-motd.d/10-orangepi-header
- Original was this:
TERM=linux toilet -f standard -F metal $(echo $BOARD_NAME | sed 's/Orange Pi/OPi/')
- Change it to this, which will output a rainbow MIKU:
TERM=linux toilet -f standard -W -F gay $(echo MIKU)
- You can add ASCII art in the same place
- Find it from places like here: http://aa.ja.utf8art.com/node/7592
- Generate from image files here: https://www.asciiart.eu/image-to-ascii - Below generates a 30 char wide cyan Miku based on the following image:
More about Security
Security is it's own, lengthy topic, because you can do a lot, or nothing, so it gets its own space. At minimum on an SBC, change the default password for root. The rest just depends on your own risk tolerance. Note that below does not include putting your SBC on the internet which is a whole other set of concerns. The "attack" here would be from your own intranet, with someone getting access to your WiFi.
- Set up basic firewall, and open just the ports you'll need, using ufw. Do below in order...
-sudo apt install ufw
-sudo ufw allow ssh
-sudo ufw enable
Ensure SSH is allowed first obv
-sudo ufw status
Check all's working
-sudo ufw allow 80
These 3 open up basic HTTP and port 8000 which ST uses
-sudo ufw allow 443
-sudo ufw allow 8000
- You can change default SSH from port 22 to something else as well, for added security. This is basic obfuscation, preventing it from being discovered casually. Any number from 1024 and 49151 will work. I'm going to use
1234
in the example below. You should pick something else...- Edit the SSH Configuration:
sudo nano /etc/ssh/sshd_config
- Find the line starting with Port 22 and change it to your desired port, e.g., Port 1234, and save the file
- Before restarting SSH, allow the new port through UFW:
sudo ufw allow 1234/tcp
- Optionally, you can remove the rule for port 22:
sudo ufw delete allow 22/tcp
- Reboot the SBC, and set up your SSH terminal to access the new port to make sure it's working.
sudo ufw status
to check status of ssh and firewall
- Edit the SSH Configuration:
- You can change the points that can access the SBC. Probably not an issue given the SBC isn't exposed to the internet, but it can also be done through the firewall. The first one sets up most home networks, the other two are for larger enterprise systems.
Adding an SSD1306 OLED display
I got annoyed having to constantly SSH into the SBC to see what's going on, so I added a 1" OLED display to it. These are inexpensive on AliExpress (~UDS$1-2 each) and easily interface to SBC and microcontrollers. The pictured one is approximately 1" square, runs an SSD1306 serial display on a 4 wire I2C interface, and has 128x64 pixels, enough for 5 lines or so.
The software below shows major items going on with the SBC, including whether ST is up, what WiFi it's connected to, how many devices are connected to it, CPU temp, CPU load, system clock, and IP it's broadcasting on. It flashes between information as this is too much for one screen.
- First run
sudo orangepi-config
(or raspberrypi-config). Navigate to System > Hardware and enable i2c0 and i2c1. Reboot- If you don't have these config tools, you can enable it by editing the appropriate configuration file (e.g., /boot/armbianEnv.txt or /boot/orangepiEnv.txt) and adding
overlays=i2c0 i2c1
manually
- If you don't have these config tools, you can enable it by editing the appropriate configuration file (e.g., /boot/armbianEnv.txt or /boot/orangepiEnv.txt) and adding
- Verify I2C is Enabled:
ls /dev/i2c-*
You should see /dev/i2c-1 or similar. - Wiring the SSD1306 Display
- VCC → 3.3V or 5V (depending on your display's specifications).
- GND → Ground.
- SDA → I2C SDA pin (e.g., GPIO pin 3 on Orange Pi).
- SCL → I2C SCL pin (e.g., GPIO pin 5 on Orange Pi).
- Install i2c-tools:
sudo apt install i2c-tools
and scan for the SSD1306 display:i2cdetect -l
(list them all)i2cdetect -y 1
(probe for given port)- You should see the address 0x3C or 0x3D in the output.
- Don't be me and spend 2 hours trying to figure out why you don't have permission to hit up HDMI on port 6, and do the list first
- Install Python and Libraries:
sudo apt install python3 python3-pip
,sudo apt-get install python3.10-dev
,pip3 install smbus2 luma.oled psutil
- Create Python script,
system_info_display.py
(see code below) - Test script:
python3 system_info_display.py
- Make it persistent: Edit crontab
crontab -e
and add line@reboot sleep 30 && python3 /path/to/system_info_display.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
from luma.core.interface.serial import i2c from luma.oled.device import ssd1306 from luma.core.render import canvas import time import subprocess import psutil from datetime import datetime, timedelta # Initialize I2C and SSD1306 device serial = i2c(port=2, address=0x3C) # Use the correct I2C bus and address device = ssd1306(serial) # Ensure this is device you're using device.contrast(128) # Set contrast 0-255 def get_wifi_info(): """Get the WiFi SSID the Orange Pi is connected to.""" try: result = subprocess.run(["iwgetid", "-r"], capture_output=True, text=True) ssid = result.stdout.strip() return f"WiFi: {ssid}" if ssid else "WiFi: Not Connected" except Exception: return "WiFi: Error" def get_connected_devices(): """Get the number of devices connected to the Orange Pi.""" try: result = subprocess.run(["arp", "-n"], capture_output=True, text=True) devices = [line.split()[0] for line in result.stdout.splitlines()[1:] if line] return f"Devices: {len(devices)}" except Exception: return "Devices: Error" def get_system_time(): """Get the current system time in 12-hour format with AM/PM.""" return datetime.now().strftime("%I:%M:%S %p") def get_uptime(): """Get the system uptime in a human-readable format.""" try: uptime_seconds = int(time.time() - psutil.boot_time()) uptime = timedelta(seconds=uptime_seconds) days = uptime.days hours, remainder = divmod(uptime.seconds, 3600) minutes, _ = divmod(remainder, 60) return f"Uptime: {days}d {hours}h {minutes}m" except Exception: return "Uptime: Error" def get_cpu_usage(): """Get the current CPU usage.""" return f"CPU: {psutil.cpu_percent()}%" def get_cpu_temperature(): """Get the CPU temperature in Celsius.""" try: with open("/sys/class/thermal/thermal_zone0/temp", "r") as f: temp = int(f.read().strip()) / 1000 # Convert millidegrees to degrees return f"Temp: {temp:.1f}°C" except Exception: return "Temp: Error" def get_ip_address(): """Get the IP address of the Orange Pi.""" try: result = subprocess.run(["hostname", "-I"], capture_output=True, text=True) ip = result.stdout.strip().split()[0] # Get the first IP address return f"IP: {ip}" if ip else "IP: Not Found" except Exception: return "IP: Error" def get_web_server_status(): """Check if the PM2 web server job is running.""" try: result = subprocess.run(["pm2", "list"], capture_output=True, text=True) if "online" in result.stdout: return "ST: Online" else: return "ST: Offline" except Exception: return "Web: Error" def display_set_1(): """Display system information on the OLED screen.""" wifi_info = get_wifi_info() devices_info = get_connected_devices() time_info = get_system_time() cpu_info = get_cpu_usage() web_info = get_web_server_status() with canvas(device) as draw: draw.text((0, 0), web_info, fill="white") draw.text((1, 0), web_info, fill="white") # Bold type draw.text((0, 12), wifi_info, fill="white") draw.text((0, 24), devices_info, fill="white") draw.text((0, 36), time_info, fill="white") draw.text((0, 48), cpu_info, fill="white") def display_set_2(): """Display system information on the OLED screen.""" wifi_info = get_wifi_info() uptime_info = get_uptime() ip_info = get_ip_address() cpu_temp = get_cpu_temperature() web_info = get_web_server_status() with canvas(device) as draw: draw.text((0, 0), web_info, fill="white") draw.text((1, 0), web_info, fill="white") # Bold type draw.text((0, 12), wifi_info, fill="white") draw.text((0, 24), uptime_info, fill="white") draw.text((0, 36), ip_info, fill="white") draw.text((0, 48), cpu_temp, fill="white") try: while True: # Display Set 1 display_set_1() time.sleep(10) # Adjust sleep time # Display Set 2 display_set_2() time.sleep(10) # Adjust sleep time except KeyboardInterrupt: device.clear() # Clear the display before exiting print("Exiting script")
- Troubleshooting
- Ensure your user is in the i2c group:
sudo usermod -aG i2c $(whoami)
- Verify that your user is in the i2c group:
groups orangepi
- Verify wiring, and ensure I2C is enabled in the system configuration
- Ensure your user is in the i2c group:
Setting Up Rotating Compressed Backups
This guide will show you how to create a rotating 7-day backup system. These backups will be stored as compressed .tar.gz files on the same machine. This setup is ideal for situations where we're more worried about accidental file deletion by ST during an update. A "true" backup would be off-machine.
- Create a directories and log file:
mkdir -p ~/backup/daily
,touch ~/backup/backup.log
- Create backup script (below is for Orange Pi, with ST installed in user account):
nano ~/backup/daily_backup.sh
- Make executable, and add it to the cron jobs:
chmod +x /backup/daily_backup.sh
,crontab -e
, add line0 2 * * * bash /home/orangepi/backup/daily_backup.sh
to make the job fire off at 2AM machine time. - This stores the files as compressed tar files. Uncompress with
tar -xzf /backup/daily/backup_X.tar.gz -C /home/orangepi/SillyTavern/data/default-user/
in case of complete loss of files (this will replace everything... consider expanding it elsewhere and reviewing materials instead at file level)
Setting Up an Express-Based Status Server via Web
I decided to add a simple web interface that would show device status, and allow me to reset the machine without the need to SSH in. The following outlines how to set up a web-based status dashboard using Express.js and Avahi for local network access via orangepi.local
. Express.js leverages the node.js framework already installed for Silly Tavern. Below assumes node.js and npm is installed along with pm2, since it was part of the earlier steps to set up ST.
1. Prepare the System
Update and Install Required Packages: ``sudo apt install -y avahi-daemon avahi-utils```
Enable Avahi for Local Network Discovery
2. Set Up the Express Server
Create the Project Directory: mkdir -p ~/status-server/public && cd ~/status-server
Initialize a Node.js Project: npm install express
Create the Server File: nano server.js
Paste the following code:
Allow Node.js to Bind to Port 80: sudo setcap 'cap_net_bind_service=+ep' $(which node)
3. Create the Web Interface
Create the HTML File: nano ~/status-server/public/index.html
Paste the following:
4. Start the Server on Boot
Start and Save the Process
Run the displayed command to finalize startup settings.
5. Test and Access the Server
Check if the Server is Running: pm2 list
6. (Optional) Enable External Access
If you've install ufm as a firewall you may need to open it: sudo ufw allow 80/tcp
7. Access the Web Interface
From another device on the same network, open: http://orangepi.local
or the URL of your headless machine. It should be working across port 80 now.
More about minimum hardware requirements
This guide was written based on my experience standing up an Orange Pi 3B with 4GB RAM and a 64GB eMMC module running Ubuntu. These specs are overkill... and I'm all about min specs and optimization, so I spent time exploring what the minimum is, both from cost and technology standpoint.
- Raspberry Pi 3 with a 16GB SD card would work, as well as other RPi alternatives available via AliExpress or similar vendors. As long as there's a modern Linux OS available for the system you should be fine; the main hangup will be ensuring node.js will run on whatever OS you use. Raspberry Pi can be expensive for what they are. The Chinese clones are often less expensive and/or have better feature sets at cost parity. Since this guide runs through the whole process, rather than providing an iso to burn that just works, I'd encourage you to check out the alternatives, because they make the RPi look dated in comparison.
- RPI 3B: $50 as of writing. Empty SD card slot as "memory", 1 GB RAM, and a shared LAN with USB that gets you 10Mb/s throughput
- OrangePi 3B: $50 gets you 4GB RAM, a 16GB eMMC module, M.2 port for SSD, and gigabit LAN. And that empty SD card space.
- OrangePi Zero 2W comes in a variety of RAM configurations 1G+, and will successfully run and serve ST based on personal experience
- Why 1GB RAM: My attempt to get it working on an RPi Zero W with 512K ran out of memory (aside from requiring unofficial repo for npm to get that working.) ST requires "only" ~350MB to run on my machine, headless; it's the runtime compile that ST is doing that prevented it from starting.
- Why 16GB storage: I've stood up SBC Ubuntu machines on 8GB. It was a hateful experience in package management. So 16GB is minimum IMHO.
- Why Ubuntu: It just works, most SBC have a version of it available, and node.js is being actively updated for it. If you know enough about Linux to have a strong opinion on another version you'd want to use, you probably don't need this guide.