Install the Box IO Docker server on Linux
These steps install the Box IO station on Ubuntu 24.04. Docker runs two containers: boxio (the hub and the dashboard API) and nginx (HTTP and HTTPS). This station’s dashboard is https://boxio.krmsproducts.net. Arduino boards use http://boxio.krmsproducts.net:5923.
The Ubuntu machine on the LAN is 192.168.10.248. The SSH user is kasey. A different Linux computer uses its own address and username in the same commands. Do not put the SSH password, the JWT secret, SMTP passwords, or Twilio tokens in git.
1. Point DNS and forward the ports
Do this before the first boot so the public name can reach the machine.
- In DNS for
krmsproducts.net, add an A record namedboxio. The value is the router’s public WAN address, not192.168.10.248. From a PC on that network,https://ifconfig.meshows the WAN address. - On a phone with Wi-Fi off,
ping boxio.krmsproducts.netmust answer from that WAN address. - Forward these TCP ports from the router to
192.168.10.248. UDP is not required.80to port 80 (HTTP, and the Let’s Encrypt check)443to port 443 (the dashboard)5923to port 5923 (Arduino and ESP32)
2. Sign in and update Ubuntu
From a PC on the same LAN:
ssh kasey@192.168.10.248
Type the account password when SSH asks. Then update the OS:
sudo apt update
sudo apt upgrade -y
3. Install Docker
Still signed in as kasey. This installs Docker Engine and the Compose plugin from Docker’s own Ubuntu repository.
sudo apt install -y ca-certificates curl git ufw
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a644 /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo ${UBUNTU_CODENAME:-$VERSION_CODENAME}) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker kasey
The docker group applies on the next login. Sign out and back in:
exit
ssh kasey@192.168.10.248
docker version
docker compose version
Both version commands should print a version. If docker says permission denied, the new login did not pick up the group yet.
4. Open the firewall
Allow SSH before you enable the firewall, or the next login can be locked out.
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 5923/tcp
sudo ufw enable
sudo ufw status
5. Copy Box IO onto the server
cd ~
git clone https://github.com/Someone275/Box_io.git box_io
cd ~/box_io/server
If clone fails, download the ZIP from GitHub, copy it to the server, unzip it, and rename the folder to box_io. Then cd ~/box_io/server.
6. Create the secret file
cd ~/box_io/server
cp .env.example .env
openssl rand -hex 48
Open .env and set JWT_SECRET to that hex string. One line, no quotes:
JWT_SECRET=paste_the_hex_here
Save the file. Never commit .env. The compose file reads this secret when the container starts. Projects, users, and device keys are stored in the Docker volume boxio-data, not in the git folder.
7. Start the containers
cd ~/box_io/server
docker compose up --build -d
docker compose ps
docker compose logs -f --tail=50
Wait until the log shows the device hub on port 5923 and the web API on port 3847. Ctrl+C stops following the log. It does not stop the containers. boxio and nginx should both say Up. They restart with the machine because the compose file uses unless-stopped.
From a PC on the same LAN, check the hub and the dashboard. The first certificate is self-signed, so the browser warning is expected until the next step.
http://192.168.10.248:5923/health
https://192.168.10.248
The health URL returns JSON with ok set to true. Port 80 redirects to HTTPS. Port 443 is the dashboard. Arduino sketches keep using HTTP on port 5923.
8. Issue the HTTPS certificate
Let’s Encrypt has to see the public name. On the server, both of these must print boxio-http-ok:
curl -sS http://127.0.0.1/http-ok
curl -sS http://192.168.10.248/http-ok
Then, from a phone with Wi-Fi off, open http://boxio.krmsproducts.net/http-ok. It must show the same text. A test from the LAN is not enough.
When that public check works, request the certificate:
cd ~/box_io/server
chmod +x nginx/init-letsencrypt.sh nginx/ensure-certs.sh nginx/issue-cert-dns.sh
DOMAIN=boxio.krmsproducts.net EMAIL=you@krmsproducts.net ./nginx/init-letsencrypt.sh
Use an email address you can read. Let’s Encrypt sends expiry notices there. If the script times out during connect, port 80 is not reachable from the internet. Use the DNS method instead. It prints a TXT name and value. Create that record, wait until dig shows it, then press Enter in the script:
cd ~/box_io/server
EMAIL=you@krmsproducts.net ./nginx/issue-cert-dns.sh
dig +short TXT _acme-challenge.boxio.krmsproducts.net
From the phone on cellular, https://boxio.krmsproducts.net should open with a normal padlock. The DNS certificate is not renewed by the certbot loop. Run issue-cert-dns.sh again before 90 days.
9. Create the admin account
- Open
https://boxio.krmsproducts.net. - Create the admin username and password. This is the first user, so the page asks for it.
- Sign in. Under device keys, generate a key. It starts with
bx_. Copy it into the sketch asBOXIO_AUTH. Do not commit that key. - Create a project and assign that device key.
- Turn on Edit, add widgets, set each virtual pin, and Save layout.
- Switch to Live before using buttons, sliders, and the input box.
- Optional: in Settings, save SMTP for email and Twilio for SMS. The email and SMS pages list those fields.
10. Update an existing station
Run this on the Ubuntu machine. git pull does not wipe boxio-data, so users, device keys, and layouts stay.
ssh kasey@192.168.10.248
cd ~/box_io
git checkout main
git pull origin main
cd server
docker compose up --build -d
docker compose ps
Hard-refresh the dashboard after both containers are Up. Sketches keep the same host, port 5923, and device key. From ~/box_io/server, docker compose restart restarts the containers, and docker compose down stops them without deleting the volume. docker compose up --build -d starts them again.
If the dashboard does not come back:
cd ~/box_io/server
docker compose logs -f --tail=80 boxio