notes / Deployment
Running a Discord Bot on OpenWrt
Field notes from deploying and maintaining a small Discord bot on an OpenWrt device, including Node.js, environment files, Docker/procd service direction, logs, restarts, and update workflow.
Why This Note Exists
OpenWrt is usually treated as router firmware, not as a general application server.
But on capable hardware, it can run small internal services if the setup is kept simple and maintainable. A Discord bot is a good example of a small service that can run continuously without needing a full VPS.
This note documents the practical side of running a small Node.js Discord bot on OpenWrt: where to place the files, how to handle environment variables, how to run it as a service, how to restart it, and how to debug common deployment issues.
The point is not that the bot runs on OpenWrt because that is impressive. The point is learning how to operate a small service on constrained infrastructure without losing track of logs, secrets, updates, and restarts.
Device Context
The setup is based on an OpenWrt device used as both network infrastructure and a lightweight service host.
In this kind of setup, the router is already important. Adding application services means extra care is needed.
The service should not make the router harder to maintain, and bot failures should not affect routing, DNS, VPN, or firewall behavior.
What This Setup Is Meant To Prove
- small services can run on OpenWrt when the device has enough resources
- router-hosted services need clean restart and log handling
- environment variables should not be hardcoded in source code
- Docker can make updates cleaner, but it adds its own operational layer
- OpenWrt
procdcan run services directly when Docker is not needed - deployment scripts reduce repeated manual commands
- logs are essential because silent bot failures are common
- service hosting on a router should stay deliberate and limited
Stack and Tools Used
Application Layer
- Node.js
- discord.js
- JavaScript
- environment variables
- bot token configuration
- command/event handling
OpenWrt Layer
- SSH
- LuCI where useful
- package management
- filesystem paths under
/opt - service management
- logs through OpenWrt tooling
- storage/overlay awareness
Deployment Layer
- Docker, optional
procd, optional.env.localor equivalent environment file- build/run/stop/restart scripts
- Git-based or copy-based update workflow
- SCP/rsync or other sync method
Troubleshooting Layer
- container logs
- service logs
- process checks
- environment file checks
- Discord command registration checks
- network/DNS time validation
Intended Build
The intended build is a small bot service that can run continuously on OpenWrt and be maintained without manually repeating a long set of commands every time.
A finished setup should allow:
- source code stored in a clear directory
- secrets stored outside the code
- predictable start/stop/restart
- logs available when the bot fails
- easy rebuild or redeploy
- clear update workflow from development machine to OpenWrt
- service recovery after reboot
- minimal impact on core router functions
Directory Layout
A clean layout is important.
Example direction:
/opt/discord-bots/
bot-name/
package.json
src/
.env.local
scripts/
The exact bot name can change, but the principle stays the same:
- application files live in one known directory
- secrets are not committed publicly
- scripts live near the project
- logs are easy to reach
- deployment path is documented
Environment Variables
Secrets should not be hardcoded.
A local environment file can store values like:
DISCORD_TOKEN=
CLIENT_ID=
GUILD_ID=
NODE_ENV=production
The bot should load the environment file at startup.
Important checks:
- the file exists on OpenWrt
- the service/container can read it
- the variable names match the code
- the token is not printed in logs
- the file is not committed to a public repository
A missing or unreadable env file is one of the easiest ways for a bot to fail after deployment.
Running Directly With Node.js
One direction is to run the bot directly with Node.js installed on OpenWrt.
This keeps the setup simple, but it depends on the OpenWrt package environment and installed Node.js version.
Basic manual shape:
node src/index.js
For production, manual execution is not enough. The bot needs to run as a managed service.
Running With OpenWrt procd
OpenWrt services normally use procd.
A procd service can:
- start the bot at boot
- restart it if it crashes
- capture stdout/stderr
- make start/stop/restart easier
- integrate with OpenWrt service commands
Example service shape:
#!/bin/sh /etc/rc.common
START=99
STOP=10
USE_PROCD=1
APP_DIR="/opt/discord-bots/bot-name"
start_service() {
procd_open_instance
procd_set_param command /usr/bin/node "$APP_DIR/src/index.js"
procd_set_param env NODE_ENV=production
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
This should be adjusted to the real project path and environment loading method.
Running With Docker
Docker is useful when you want a more consistent runtime.
A Docker setup can define the Node.js version, dependencies, environment file usage, and restart behavior more cleanly.
Example direction:
Dockerfile
.env.local
docker build
docker run --env-file .env.local
For OpenWrt, Docker support depends on device resources, storage, kernel support, and package availability.
Docker makes application packaging cleaner, but it also adds:
- image rebuilds
- container cleanup
- volume/env handling
- container logs
- networking decisions
- storage usage
Docker Network Choice
For a small bot, host networking may be simple:
--network host
This avoids some container network complexity, but it should be used intentionally.
The bot usually only needs outbound access to Discord, so it does not need public inbound ports.
Service and Container Management
A useful deployment should include scripts for common actions.
Examples:
build
run
stop
restart
logs
status
clean
help
The goal is to avoid remembering long Docker or service commands manually.
A helper script should be clear enough that future updates are not painful.
Deployment Workflow
A practical workflow looks like this:
- edit code on the development machine
- test locally where possible
- sync files to OpenWrt
- install dependencies or rebuild container
- restart the service/container
- check logs
- verify the bot is online
- test one real command/action
File syncing can use:
scprsync- Git pull if available
- manual upload for small changes
The exact method matters less than consistency.
Common Failure Points
Bot Starts Locally but Not on OpenWrt
Likely causes:
- wrong Node.js version
- missing dependencies
- wrong working directory
- missing
.env.local - file permissions
- OpenWrt path differences
- service not loading environment variables
Container Builds but Does Not Run
Likely causes:
- wrong image architecture
- missing command
- missing env file
- wrong container name
- old container already exists
- dependency install failure
- application exits immediately
Useful checks:
docker ps -a
docker logs <container-name>
Bot Is Online but Slash Commands Are Wrong
Likely causes:
- command registration not redeployed
- duplicate command names
- commands registered globally but expected instantly
- old commands still cached
- wrong application/client ID
- wrong guild ID
- bot lacks permissions
Service Runs but Logs Are Missing
Likely causes:
- stdout/stderr not captured
- service file does not enable logging
- container logs not checked
- application catches errors silently
- process exits before logging
Time or Certificate Errors
A device with wrong system time can cause TLS/certificate problems.
Symptoms may look unrelated, such as network requests failing even though DNS and internet access work.
Useful checks:
date
logread | grep -i cert
If time is wrong, fix NTP/time sync first.
Practical Decisions
Keep the router’s main job protected
The router should still be a router first.
If the bot becomes heavy, unstable, or complicated, it may be better to move it to a small server or VPS.
Do not hardcode secrets
Tokens and IDs should live in environment files or secret handling, not inside code.
Use scripts for repeated actions
If a command is repeated often, make it a script.
This reduces mistakes during rebuilds, restarts, and cleanup.
Prefer clear logs over silent restarts
A service that restarts automatically but gives no useful logs is hard to maintain.
Logs should make failures obvious.
Avoid exposing ports unnecessarily
Most Discord bots only need outbound access.
Do not open public ports unless the bot runs a web server or webhook listener that actually needs inbound traffic.
Keep one clean source of deployment truth
Avoid having multiple half-working deployment methods at the same time.
If Docker is used, make Docker the main path. If procd direct Node.js is used, make that the main path.
What A Finished Setup Should Show
A strong finished setup should show:
- bot source files in a clear directory
- environment file present and protected
- service/container starts reliably
- restart workflow documented
- logs available
- bot comes online after reboot or restart
- deployment/update script available
- command registration understood
- no unnecessary public ports exposed
- router core functions unaffected
- cleanup process for old containers/images if using Docker
Evidence Worth Capturing
Useful evidence for this note would include:
- directory layout screenshot or tree
- service file example
- Dockerfile
- helper script
.env.examplewithout real secretsdocker psoutput- service status output
- log examples
- bot online screenshot
- command test screenshot
- restart test
- notes about failures and fixes
Technical Assumptions
This setup assumes the OpenWrt device has enough CPU, RAM, and storage for the bot and runtime.
It also assumes that the bot is small and mostly event/command based, not a heavy application.
The setup assumes that internet access, DNS, and system time are stable, because Discord API access depends on all three.
Key Risks
- router overloaded by application services
- secrets accidentally committed or printed
- bot failing silently after restart
- old containers conflicting with new ones
- wrong architecture image
- missing environment variables
- storage filling up with images/logs
- DNS/time issues causing API failures
- command registration confusion
- no clean rollback after a broken update
Current State
This note represents the deployment and maintenance direction for running a small Discord bot on OpenWrt.
The important lesson is that even a small bot needs operational structure when it runs continuously: environment files, service management, restart behavior, logs, and repeatable deployment steps.
This connects directly to other notes about OpenWrt, Docker scripts, DNS, and service troubleshooting.
What This Note Does Not Claim
This note does not claim that OpenWrt is always the best place to host bots.
It does not claim that router-hosted services are suitable for every production workload.
It does not claim that Docker is mandatory.
The project is best understood as a practical deployment note for small services on constrained infrastructure.
Practical Takeaway
Running a bot on OpenWrt is not mainly about starting node index.js.
The useful part is the operational structure around it:
- where the files live
- how secrets are loaded
- how it starts after reboot
- how it restarts after failure
- where logs are read
- how updates are deployed
- how to clean up broken containers
- how to avoid affecting the router’s main job
That is what makes the setup maintainable.