Box IO

Self-hosted IoT for Arduino and ESP32.

Install the Box IO Docker server on CentOS

These steps install the prebuilt image ghcr.io/someone275/box_io-docker-server on CentOS Stream 9 or CentOS Stream 10. Docker runs two containers: boxio (the hub and the dashboard API) and nginx (HTTP and HTTPS). Arduino boards use HTTP port 5923. Ubuntu is on the Ubuntu page. Red Hat Enterprise Linux is on the Red Hat page. A BlueOnyx panel already owns ports 80 and 443, so use the BlueOnyx page there.

Replace boxio.example.com with the dashboard name, and user with the SSH account. Do not put the SSH password, the JWT secret, SMTP passwords, or Twilio tokens in git.

1. Point DNS and forward the ports

  1. Add an A record for boxio.example.com. The value is the router’s public WAN address.
  2. From a phone with Wi-Fi off, ping boxio.example.com must answer from that WAN address.
  3. Forward TCP 80, 443, and 5923 from the router to this CentOS machine. UDP is not required.

2. Sign in and update CentOS

ssh user@boxio.example.com
sudo dnf -y upgrade

3. Install Docker Engine

This installs Docker Engine and the Compose plugin from Docker’s CentOS repository. CentOS Stream uses dnf.

sudo dnf -y install dnf-plugins-core git
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker user

The docker group applies on the next login. Sign out and back in, then check both version commands:

exit
ssh user@boxio.example.com
docker version
docker compose version

4. Open firewalld

Leave SSH reachable. firewall-cmd adds the dashboard ports and the Arduino hub. If firewalld is not running, the host firewall still has to allow these ports.

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=5923/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

SELinux can stay enforcing. The compose file labels the nginx files so the container can read them.

5. Copy Box IO onto the server

cd ~
git clone https://github.com/Someone275/Box_IO-Docker-server.git
cd ~/Box_IO-Docker-server

The clone holds the compose file and the HTTPS proxy. docker load installs the prebuilt image. This machine does not compile Node.

6. Create the secret file

cd ~/Box_IO-Docker-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. Projects, users, and device keys are stored in the Docker volume boxio-data.

7. Start the containers

cd ~/Box_IO-Docker-server
curl -fL -o box_io-docker-server.tar.gz https://github.com/Someone275/Box_IO-Docker-server/releases/download/v1.0.0/box_io-docker-server.tar.gz
docker load -i box_io-docker-server.tar.gz
docker compose up -d --no-build
docker compose ps
docker compose logs -f --tail=50

boxio and nginx should both say Up. The health URL on the LAN returns JSON with ok set to true. The first certificate is self-signed, so a browser warning is expected until the next step.

http://127.0.0.1:5923/health
https://boxio.example.com

8. Issue the HTTPS certificate

Let’s Encrypt has to see the public name. On the server, curl -sS http://127.0.0.1/http-ok must print boxio-http-ok. From a phone on cellular, http://boxio.example.com/http-ok must show the same text.

cd ~/Box_IO-Docker-server
chmod +x nginx/init-letsencrypt.sh nginx/ensure-certs.sh nginx/issue-cert-dns.sh
DOMAIN=boxio.example.com EMAIL=you@example.com ./nginx/init-letsencrypt.sh

If that times out, port 80 is not reachable from the internet. Use the DNS method. It prints a TXT name and value. Create that record, wait until dig shows it, then press Enter:

cd ~/Box_IO-Docker-server
EMAIL=you@example.com ./nginx/issue-cert-dns.sh
dig +short TXT _acme-challenge.boxio.example.com

9. Create the admin account

  1. Open https://boxio.example.com and create the admin username and password.
  2. Generate a device key. It starts with bx_. Copy it into the sketch as BOXIO_AUTH.
  3. Create a project, add widgets, Save layout, then switch to Live.
  4. Open License and install a trial or a paid year before pin values will show.

10. Update

cd ~/Box_IO-Docker-server
git checkout main
git pull origin main
curl -fL -o box_io-docker-server.tar.gz https://github.com/Someone275/Box_IO-Docker-server/releases/download/v1.0.0/box_io-docker-server.tar.gz
docker load -i box_io-docker-server.tar.gz
docker compose up -d --no-build
docker compose ps

git pull updates the proxy files. docker load updates the image. The volume boxio-data stays. From this directory, docker compose restart restarts the containers, and docker compose down stops them without deleting the volume. After the image is on Docker Hub, set BOXIO_IMAGE in .env and use docker compose pull instead of curl and docker load.