Files
rv50x-manager/README-docker.md
D Stephenson 6c21525b79 Initial commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 20:43:59 +00:00

20 KiB

RV50x Template Manager — Docker Edition

A containerized web-based tool for managing configuration templates on Sierra Wireless AirLink RV50x modems. Runs as a full Docker stack including the web application, NocoDB, and PostgreSQL — start and stop the entire thing with a single command.


Table of Contents

  1. Stack Overview
  2. Requirements
  3. Project Structure
  4. First-Time Setup
  5. The .env File
  6. Building and Starting
  7. NocoDB Setup
  8. Updating the App
  9. Managing the Stack
  10. Using Portainer
  11. Migrating to a New Machine
  12. Reconnecting to a New NocoDB Instance
  13. Data and Volumes
  14. Troubleshooting

Stack Overview

The stack runs three containers:

Container Image Purpose Default Port
rv50x-manager Built from Dockerfile FastAPI web app + Playwright Your choice
rv50x-nocodb nocodb/nocodb:latest NocoDB UI and API 8090
rv50x-postgres postgres:16-alpine PostgreSQL database for NocoDB Internal only

All three containers communicate over a private internal Docker network. PostgreSQL is never exposed to the host — only NocoDB can reach it. The web app talks to NocoDB via the internal hostname nocodb.

Your Browser
     │
     ▼
rv50x-manager (YOUR_PORT) ──→ nocodb (8090) ──→ postgres (internal)
     │
     ▼
Your Modems (port 443, via Playwright)

Requirements

  • Docker Engine 24+ or Docker Desktop
  • Docker Compose v2 (docker compose or docker-compose)
  • Network access from the Docker host to your modems on port 443
  • Portainer (optional, but recommended for easy management)

Project Structure

/opt/rv50x-manager/
├── app.py                  ← FastAPI backend
├── index.html              ← Web UI
├── requirements.txt        ← Python dependencies
├── Dockerfile              ← Container build instructions
├── docker-compose.yml      ← Stack definition
├── .env                    ← Your secrets and config (never commit this)
├── .env.example            ← Template for .env
└── .dockerignore           ← Files excluded from the Docker image

Not needed in the Docker folder (kept separately if you want CLI access):

  • download.py / upload.py — standalone CLI scripts
  • modems.csv — legacy device list
  • .venv/ — Python virtual environment

First-Time Setup

Step 1 — Install Docker

Fedora / RHEL / Rocky:

sudo dnf install -y docker docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER   # log out and back in after this

Debian / Ubuntu:

sudo apt-get install -y docker.io docker-compose-plugin
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER

Verify Docker is working:

docker run hello-world

Step 2 — Copy project files to the host machine

Create the install directory and set permissions, then copy your files:

# Create the directory and give your user ownership
sudo mkdir -p /opt/rv50x-manager
sudo chown $USER:$USER /opt/rv50x-manager

# Copy all project files into it
cp app.py index.html requirements.txt Dockerfile \
   docker-compose.yml .env.example .dockerignore \
   /opt/rv50x-manager/

cd /opt/rv50x-manager

Step 3 — Create your .env file

cd /opt/rv50x-manager
cp .env.example .env
nano .env    # or use any text editor

See The .env File section below for what to put in each field.

Step 4 — Set your port

Open /opt/rv50x-manager/docker-compose.yml and find this line under rv50x-manager:

ports:
  - "YOUR_PORT:8000"

Replace YOUR_PORT with the port number you want to use on the host machine. For example:

ports:
  - "8001:8000"

The app will then be accessible at http://host-ip:8001.

Step 5 — Build and start

cd /opt/rv50x-manager
docker-compose up -d

This builds the rv50x-manager image and starts all three containers. The first build takes a few minutes because it downloads the Playwright base image and installs Chromium.

Step 6 — Set up NocoDB

See NocoDB Setup below.

Step 7 — Update .env with NocoDB IDs

After importing your data into NocoDB, update .env with the real IDs and restart the manager:

docker-compose restart rv50x-manager

Step 8 — Open the app

http://your-docker-host-ip:YOUR_PORT

The .env File

Create this file by copying .env.example and filling in your values. It is read automatically by docker-compose. Never commit this file to version control — it contains passwords and API tokens.

# ── NocoDB connection ──────────────────────────────────────────────────────
#
# Use "http://nocodb:8080" to connect to the NocoDB container in this stack.
# Use your external NocoDB URL if you prefer to point at an existing instance.
NOCODB_URL=http://nocodb:8080

# Your NocoDB API token.
# Get it from: NocoDB → Profile (bottom-left) → Team & Settings → API Tokens
# Tokens look like: eWU_ilelaCtNy1JzC7vf41DokkqFOovcLHM0zVml
NOCODB_TOKEN=your-api-token-here

# These three IDs come from the NocoDB browser URL after you import your data.
# The URL structure is:
#   http://host:8090/{org_id}/{BASE_ID}/{TABLE_ID}/{VIEW_ID}/table-name
# Leave as placeholder for now — update after NocoDB setup (Step 6 above).
NOCODB_BASE_ID=your-base-id-here
NOCODB_TABLE_ID=your-table-id-here
NOCODB_VIEW_ID=your-view-id-here

# ── PostgreSQL ─────────────────────────────────────────────────────────────
# Internal password used between NocoDB and PostgreSQL containers only.
# You will never need to type this manually — make it long and strong.
POSTGRES_PASSWORD=SomeLongStrongPassword123!

# ── NocoDB JWT secret ──────────────────────────────────────────────────────
# Any long random string used to sign NocoDB authentication tokens.
# Generate one with: openssl rand -hex 32
NC_JWT_SECRET=paste-a-long-random-string-here

# ── Playwright timeouts (optional) ────────────────────────────────────────
# Uncomment and adjust if your modems are slow to respond. Values in ms.
# PAGE_TIMEOUT=90000
# DOWNLOAD_TIMEOUT=120000
# UPLOAD_TIMEOUT=120000
# MAX_RETRIES=3

Generating secrets

# Generate a strong JWT secret
openssl rand -hex 32

# Generate a strong password
openssl rand -base64 24

Building and Starting

Build the image

docker-compose build

Only needed when app.py, index.html, or requirements.txt change. Skipped automatically if you just change .env.

Start the stack

docker-compose up -d

The -d flag runs containers in the background (detached mode). Without it the logs stream to your terminal and the stack stops when you close the terminal.

Check that everything is running

docker-compose ps

You should see all three containers with status running or healthy.

View logs

# All containers
docker-compose logs -f

# Just the web app
docker-compose logs -f rv50x-manager

# Just NocoDB
docker-compose logs -f rv50x-nocodb

Stop the stack

docker-compose stop

Stops all containers. Data is preserved in volumes. Start again with docker-compose up -d.

Remove containers (keep data)

docker-compose down

Removes containers but keeps all named volumes (your modem data, templates, downloads). Safe to run before a rebuild.

Remove everything including data ⚠

docker-compose down -v

This deletes all volumes including your NocoDB database and all template files. Only use this if you want a completely clean slate.


NocoDB Setup

After starting the stack for the first time, NocoDB needs to be configured before the web app can use it.

Step 1 — Open NocoDB

http://your-docker-host-ip:8090

Step 2 — Create your account

On first launch NocoDB will prompt you to create an admin account. Use a strong password and note the credentials.

Step 3 — Create a new base

Click + New Base and name it something like Cell Modems.

Step 4 — Import your modem data from CSV

  1. Export your current modem data from your existing NocoDB instance: toolbar → Download → CSV
  2. In the new NocoDB, click + Add or importImport from CSV
  3. Upload the CSV file
  4. NocoDB will auto-detect all columns including hostname, ip_address, dept, password, etc.
  5. Confirm the import

Step 5 — Recreate filtered views

The CSV import brings the data but not the views. Recreate them manually:

Electric view:

  1. In the left sidebar, click + Add ViewGrid
  2. Name it Electric
  3. Click Filter+ Add Filter
  4. Set: dept is ELEC

Gas & Water view:

  1. Click + Add ViewGrid
  2. Name it Gas & Water
  3. Click Filter+ Add Filter
  4. Set: dept is GW

Step 6 — Get the IDs from the browser URL

Navigate to your Cell Modems table. The URL will look like:

http://host:8090/abc123def/BASE_ID_HERE/TABLE_ID_HERE/VIEW_ID_HERE/cell-modems

Copy BASE_ID_HERE, TABLE_ID_HERE, and VIEW_ID_HERE (use the All view ID).

Step 7 — Generate an API token

  1. Click your profile avatar (bottom-left)
  2. Go to Team & Settings → API Tokens
  3. Click Add Token, give it a name like rv50x-manager
  4. Copy the token

Step 8 — Update .env and restart

cd /opt/rv50x-manager
nano .env
# Update NOCODB_TOKEN, NOCODB_BASE_ID, NOCODB_TABLE_ID, NOCODB_VIEW_ID

docker-compose restart rv50x-manager

Step 9 — Verify the connection

Open the web app and check that the device groups show the correct counts. If devices appear, the connection is working.


Updating the App

When app.py or index.html change:

# Stop the stack
docker-compose stop

# Rebuild the manager image (NocoDB and Postgres don't need rebuilding)
docker-compose build rv50x-manager

# Start everything back up
docker-compose up -d

When only .env changes (NocoDB IDs, token, timeouts):

docker-compose restart rv50x-manager

When requirements.txt changes (new Python packages):

docker-compose build rv50x-manager
docker-compose up -d

Managing the Stack

Start individual services

docker-compose up -d postgres    # start just postgres
docker-compose up -d nocodb      # start just nocodb
docker-compose up -d rv50x-manager  # start just the web app

Restart a single service

docker-compose restart rv50x-manager

Rebuild and restart a single service

docker-compose up -d --build rv50x-manager

Execute a command inside a running container

# Open a shell in the web app container
docker exec -it rv50x-manager bash

# Check Python packages installed
docker exec rv50x-manager pip list

# Test NocoDB connection from inside the container
docker exec rv50x-manager curl -s http://nocodb:8080/api/v1/health

Access the PostgreSQL database directly

docker exec -it rv50x-postgres psql -U nocodb -d nocodb

Using Portainer

Portainer gives you a browser-based UI to manage the entire stack without needing SSH.

Install Portainer (if not already installed)

docker volume create portainer_data
docker run -d \
  -p 9000:9000 \
  --name portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

Then open http://host-ip:9000 and create your admin account.

Deploy the stack via Portainer

  1. Go to Stacks → + Add Stack
  2. Name it rv50x
  3. Paste the contents of docker-compose.yml into the editor
  4. Scroll down to Environment Variables
  5. Click + Add an environment variable for each line in your .env file:
Name Value
NOCODB_URL http://nocodb:8080
NOCODB_TOKEN your token
NOCODB_BASE_ID your base ID
NOCODB_TABLE_ID your table ID
NOCODB_VIEW_ID your view ID
POSTGRES_PASSWORD your password
NC_JWT_SECRET your secret
  1. Click Deploy the stack

Start and stop via Portainer

  • Stacks → rv50x → Start — starts all containers
  • Stacks → rv50x → Stop — stops all containers
  • Individual containers: Containers list → click the start/stop icons

Update the stack via Portainer

  1. Stacks → rv50x → Editor
  2. Paste updated docker-compose.yml
  3. Click Update the stack

For code changes (app.py, index.html), you need to rebuild the image first from the command line:

docker-compose build rv50x-manager

Then update the stack in Portainer to pick up the new image.


Migrating to a New Machine

Step 1 — Export your NocoDB data

Before migrating, export your modem data as CSV from the current NocoDB instance so you can reimport it on the new machine.

In NocoDB: Cell Modems table → toolbar → Download → CSV

Step 2 — Copy project files

From the old machine:

scp app.py index.html requirements.txt Dockerfile docker-compose.yml \
    .env.example .dockerignore youruser@new-host:/tmp/rv50x-transfer/

Do not copy .env over an insecure connection — recreate it manually on the new machine.

On the new machine, move files into place:

sudo mkdir -p /opt/rv50x-manager
sudo chown $USER:$USER /opt/rv50x-manager
cp /tmp/rv50x-transfer/* /opt/rv50x-manager/

Step 3 — Copy template files (optional)

If you want to keep your existing XML templates, downloaded configs, and upload files:

# Export volumes from the old machine
cd /opt/rv50x-manager

docker run --rm \
  -v rv50x-manager_xml_templates:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/xml_templates.tar.gz -C /data .

docker run --rm \
  -v rv50x-manager_template_downloads:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/template_downloads.tar.gz -C /data .

docker run --rm \
  -v rv50x-manager_template_uploads:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/template_uploads.tar.gz -C /data .

# Copy archives to new machine
scp xml_templates.tar.gz template_downloads.tar.gz template_uploads.tar.gz \
    youruser@new-host:/opt/rv50x-manager/

Step 4 — Set up the new machine

# Install Docker (see First-Time Setup above)

# Files should already be in /opt/rv50x-manager from Step 2
cd /opt/rv50x-manager

# Create fresh .env with new passwords and secrets
cp .env.example .env
nano .env

# Set your port
nano docker-compose.yml

# Build and start
docker-compose up -d

Step 5 — Restore template files (if copied)

cd /opt/rv50x-manager

# Restore xml_templates
docker run --rm \
  -v rv50x-manager_xml_templates:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/xml_templates.tar.gz -C /data

# Restore template_downloads
docker run --rm \
  -v rv50x-manager_template_downloads:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/template_downloads.tar.gz -C /data

# Restore template_uploads
docker run --rm \
  -v rv50x-manager_template_uploads:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/template_uploads.tar.gz -C /data

Step 6 — Set up NocoDB on the new machine

Follow the NocoDB Setup section — import your CSV, recreate views, get new IDs.

Step 7 — Update .env with new NocoDB IDs

nano .env
docker-compose restart rv50x-manager

Reconnecting to a New NocoDB Instance

If your NocoDB database is corrupted, rebuilt, or moved to a new server:

Step 1 — Get the new connection details

  1. Open the new NocoDB in your browser
  2. Import your modem data from CSV
  3. Recreate the Electric and Gas & Water filtered views
  4. Go to Profile → Team & Settings → API Tokens → create a new token
  5. Copy the base ID, table ID, and view ID from the browser URL

Step 2 — Test the connection

curl -H "xc-token: YOUR_NEW_TOKEN" \
  "http://new-nocodb-host:8090/api/v1/db/data/noco/BASE_ID/TABLE_ID?limit=1"

A successful response returns JSON with a list array containing your first modem row.

Step 3 — Update .env

cd /opt/rv50x-manager
nano .env

Update these values:

NOCODB_URL=http://new-host:8090        # or http://nocodb:8080 if using the stack
NOCODB_TOKEN=your-new-token
NOCODB_BASE_ID=your-new-base-id
NOCODB_TABLE_ID=your-new-table-id
NOCODB_VIEW_ID=your-new-view-id

Step 4 — Restart the manager

cd /opt/rv50x-manager
docker-compose restart rv50x-manager

Or in Portainer: Stacks → rv50x → rv50x-manager → Restart


Data and Volumes

All persistent data lives in Docker named volumes. They survive docker-compose down and rebuilds, and are only deleted with docker-compose down -v.

Volume Contents Maps to container path
rv50x_template_manager_postgres_data NocoDB database /var/lib/postgresql/data
rv50x_template_manager_nocodb_data NocoDB config and uploads /usr/app/data
rv50x_template_manager_template_downloads Downloaded modem configs + reports /data/template_downloads
rv50x_template_manager_template_uploads Staged XML files for upload + reports /data/template_uploads
rv50x_template_manager_xml_templates Your XML builder templates /data/xml_templates

List all volumes

docker volume ls | grep rv50x

Back up a volume

# Backs up the xml_templates volume to a tar file in the current directory
docker run --rm \
  -v rv50x_template_manager_xml_templates:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/xml_templates_backup.tar.gz -C /data .

Back up the NocoDB database

cd /opt/rv50x-manager
docker exec rv50x-postgres \
  pg_dump -U nocodb nocodb > nocodb_backup_$(date +%Y%m%d).sql

Restore the NocoDB database

cd /opt/rv50x-manager
docker exec -i rv50x-postgres \
  psql -U nocodb nocodb < nocodb_backup_20260413.sql

Troubleshooting

Symptom Likely cause Fix
docker-compose: command not found Using older Docker without compose plugin Use docker compose (space not hyphen) or install docker-compose-plugin
Build fails with "no space left on device" Docker image cache full Run docker system prune to free space
rv50x-manager exits immediately after start App crash on startup — bad .env values Run docker-compose logs rv50x-manager to see the error
NocoDB shows "Service Unavailable" Postgres not ready yet Wait 30s and refresh — healthcheck retries handle this automatically
Web app shows 0 devices NocoDB IDs wrong or token invalid Test with curl (see Reconnecting section), update .env, restart manager
ERR_BASE_NOT_FOUND in curl test Wrong NOCODB_BASE_ID Re-read the ID from the NocoDB browser URL
ERR_AUTHENTICATION_REQUIRED Wrong or expired token Regenerate token in NocoDB → API Tokens
Modems show in All but not Electric/Gas & Water Wrong dept field values Check actual dept values in NocoDB — must be exactly ELEC and GW
Playwright / Chromium crashes Missing system library The Playwright base image should have everything — check docker-compose logs rv50x-manager
Template upload times out Modem slow or unreachable Increase UPLOAD_TIMEOUT in .env, restart manager
Can't reach NocoDB at port 8090 Port conflict or firewall Change the left port in docker-compose.yml under nocodb: ports
Can't reach web app Port conflict or firewall Change YOUR_PORT in docker-compose.yml
permission denied on docker commands User not in docker group Run sudo usermod -aG docker $USER then log out and back in
Volume data missing after docker-compose down Used down -v by mistake Data is gone — restore from backup or re-import CSV