Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
- Stack Overview
- Requirements
- Project Structure
- First-Time Setup
- The .env File
- Building and Starting
- NocoDB Setup
- Updating the App
- Managing the Stack
- Using Portainer
- Migrating to a New Machine
- Reconnecting to a New NocoDB Instance
- Data and Volumes
- 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 composeordocker-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 scriptsmodems.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
- Export your current modem data from your existing NocoDB instance: toolbar → Download → CSV
- In the new NocoDB, click + Add or import → Import from CSV
- Upload the CSV file
- NocoDB will auto-detect all columns including
hostname,ip_address,dept,password, etc. - Confirm the import
Step 5 — Recreate filtered views
The CSV import brings the data but not the views. Recreate them manually:
Electric view:
- In the left sidebar, click + Add View → Grid
- Name it
Electric - Click Filter → + Add Filter
- Set:
deptisELEC
Gas & Water view:
- Click + Add View → Grid
- Name it
Gas & Water - Click Filter → + Add Filter
- Set:
deptisGW
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
- Click your profile avatar (bottom-left)
- Go to Team & Settings → API Tokens
- Click Add Token, give it a name like
rv50x-manager - 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
- Go to Stacks → + Add Stack
- Name it
rv50x - Paste the contents of
docker-compose.ymlinto the editor - Scroll down to Environment Variables
- Click + Add an environment variable for each line in your
.envfile:
| 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 |
- 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
- Stacks → rv50x → Editor
- Paste updated
docker-compose.yml - 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
- Open the new NocoDB in your browser
- Import your modem data from CSV
- Recreate the Electric and Gas & Water filtered views
- Go to Profile → Team & Settings → API Tokens → create a new token
- 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 |