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

731 lines
20 KiB
Markdown

# 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](#stack-overview)
2. [Requirements](#requirements)
3. [Project Structure](#project-structure)
4. [First-Time Setup](#first-time-setup)
5. [The .env File](#the-env-file)
6. [Building and Starting](#building-and-starting)
7. [NocoDB Setup](#nocodb-setup)
8. [Updating the App](#updating-the-app)
9. [Managing the Stack](#managing-the-stack)
10. [Using Portainer](#using-portainer)
11. [Migrating to a New Machine](#migrating-to-a-new-machine)
12. [Reconnecting to a New NocoDB Instance](#reconnecting-to-a-new-nocodb-instance)
13. [Data and Volumes](#data-and-volumes)
14. [Troubleshooting](#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:**
```bash
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:**
```bash
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:
```bash
docker run hello-world
```
### Step 2 — Copy project files to the host machine
Create the install directory and set permissions, then copy your files:
```bash
# 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
```bash
cd /opt/rv50x-manager
cp .env.example .env
nano .env # or use any text editor
```
See [The .env File](#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`:
```yaml
ports:
- "YOUR_PORT:8000"
```
Replace `YOUR_PORT` with the port number you want to use on the host machine. For example:
```yaml
ports:
- "8001:8000"
```
The app will then be accessible at `http://host-ip:8001`.
### Step 5 — Build and start
```bash
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](#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:
```bash
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.
```bash
# ── 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
```bash
# Generate a strong JWT secret
openssl rand -hex 32
# Generate a strong password
openssl rand -base64 24
```
---
## Building and Starting
### Build the image
```bash
docker-compose build
```
Only needed when `app.py`, `index.html`, or `requirements.txt` change. Skipped automatically if you just change `.env`.
### Start the stack
```bash
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
```bash
docker-compose ps
```
You should see all three containers with status `running` or `healthy`.
### View logs
```bash
# 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
```bash
docker-compose stop
```
Stops all containers. Data is preserved in volumes. Start again with `docker-compose up -d`.
### Remove containers (keep data)
```bash
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 ⚠
```bash
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 import** → **Import 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 View** → **Grid**
2. Name it `Electric`
3. Click **Filter****+ Add Filter**
4. Set: `dept` `is` `ELEC`
**Gas & Water view:**
1. Click **+ Add View** → **Grid**
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
```bash
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:
```bash
# 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):
```bash
docker-compose restart rv50x-manager
```
When `requirements.txt` changes (new Python packages):
```bash
docker-compose build rv50x-manager
docker-compose up -d
```
---
## Managing the Stack
### Start individual services
```bash
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
```bash
docker-compose restart rv50x-manager
```
### Rebuild and restart a single service
```bash
docker-compose up -d --build rv50x-manager
```
### Execute a command inside a running container
```bash
# 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
```bash
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)
```bash
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 |
6. 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:
```bash
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:
```bash
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:
```bash
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:
```bash
# 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
```bash
# 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)
```bash
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](#nocodb-setup) section — import your CSV, recreate views, get new IDs.
### Step 7 — Update .env with new NocoDB IDs
```bash
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
```bash
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
```bash
cd /opt/rv50x-manager
nano .env
```
Update these values:
```bash
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
```bash
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
```bash
docker volume ls | grep rv50x
```
### Back up a volume
```bash
# 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
```bash
cd /opt/rv50x-manager
docker exec rv50x-postgres \
pg_dump -U nocodb nocodb > nocodb_backup_$(date +%Y%m%d).sql
```
### Restore the NocoDB database
```bash
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 |