Box IO

Self-hosted IoT for Arduino and ESP32.

Install the Box IO Docker server on Red Hat

These steps install the prebuilt image ghcr.io/someone275/box_io-docker-server on Red Hat Enterprise Linux 8, 9, or 10. Docker runs two containers: boxio (the hub and the dashboard API) and nginx (HTTP and HTTPS). Arduino boards use HTTP port 5923. Leave SELinux enforcing. Ubuntu is on the Ubuntu page, CentOS Stream is on the CentOS page, and a BlueOnyx panel is on the BlueOnyx page.

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 Red Hat machine. UDP is not required.

2. Sign in and update Red Hat

Register the system with a Red Hat subscription first if dnf cannot see the BaseOS repository. Then update:

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

3. Install Docker Engine

This installs Docker Engine and the Compose plugin from Docker’s Red Hat repository. It is Docker, not Podman, so the compose file in the Box IO repository matches the commands below.

sudo dnf -y install dnf-plugins-core git
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/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
getenforce

getenforce should print Enforcing. Do not turn SELinux off for this install.

4. Open firewalld

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

The compose file mounts the nginx config with the shared SELinux label (z). Docker can read those files while SELinux stays enforcing. Named volume boxio-data does not need an extra label.

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 returns JSON with ok set to true. The first certificate is self-signed, so a browser warning is expected until the next step.

curl -sS http://127.0.0.1:5923/health
https://boxio.example.com

If nginx exits immediately and the log mentions permission denied on nginx.conf, confirm the compose volumes still end with z and that getenforce is Enforcing or Permissive. Then run docker compose up -d --no-build again.

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.