Files
alwisp/README.md
Claude ab490dd679 Rewrite roadmap to reflect B2B networking services focus
Removed ISP-generic items (residential/business plan tiers, coverage
zone polygons, address lookup, subscription billing setup). Updated
milestones to match what the site actually is — a B2B networking
services and integration company:

- Milestone 1: marked services page and staff inbox as complete
- Milestone 2: renamed Content & Plans → Content & Branding; replaced
  plan tier items with real content goals (about, contact info, logo,
  stats, email notifications)
- Milestone 3: renamed Coverage Map → Portfolio & Project Showcase;
  replaced ISP coverage tools with project case studies and per-service
  detail pages
- Milestone 4: reframed Customer Portal as Client Portal with project
  tracking and network documentation instead of billing dashboard
- Milestone 5: simplified Billing → Invoicing & Payments; removed
  recurring subscription framing, kept retainer + one-time invoices
- Milestones 6–8: unchanged, remain applicable

https://claude.ai/code/session_015wpwmheufcxkBuXivrSHhd
2026-03-01 03:18:08 +00:00

13 KiB
Raw Blame History

ALWISP Mesh Network Solutions

Dockerized LAMP stack website for Alabama's wireless ISP and mesh networking company.


Table of Contents


Stack Overview

Container Image Purpose
alwisp_web PHP 8.2 + Apache Serves the website
alwisp_db MySQL 8.0 Database (internal only)
alwisp_pma phpMyAdmin (optional) DB admin UI on port 8080

Each container is deployed individually through the Unraid Container Builder and connected via the br0 bridge network, giving both the web container and the database container their own dedicated LAN IP addresses. The web container reaches the database by its fixed LAN IP. Data persists in a named Docker volume (db_data) and survives container restarts and rebuilds.


Installation Unraid

Prerequisites

  • Unraid 6.10 or later
  • Community Applications plugin installed
  • Docker enabled (Settings → Docker → Enable Docker: Yes)
  • User Scripts plugin (recommended, for scheduled tasks)
  • br0 enabled — your Unraid host NIC must be bridged (Settings → Network → Enable Bridging: Yes)
  • Decide on two free LAN IPs before you start — one for the web container, one for the database. Example: 192.168.1.100 (web) and 192.168.1.101 (db). Reserve these in your router's DHCP settings so they are never auto-assigned.

Step 1 Clone the repository onto your Unraid cache drive

Unraid volume mount requirement: Docker containers on Unraid cannot reliably access /mnt/user/ paths. /mnt/user is a FUSE filesystem that is not available inside the Docker container namespace — the container sees an empty directory, Apache finds no files, and returns a 403 Forbidden. Always clone and mount from the cache drive path directly (/mnt/cache/appdata/).

Open an Unraid terminal (Tools → Terminal or SSH in):

cd /mnt/cache/appdata
git clone https://github.com/jasonMPM/alwisp.git
cd alwisp

Step 2 Build the Docker image

The web container uses a custom image built from the included Dockerfile. Build it once from the terminal — Unraid's Container Builder will reference it by name.

cd /mnt/cache/appdata/alwisp
docker build -t alwisp_web:latest .

After any code change to the Dockerfile, re-run this command and then restart the container from the Docker tab.


Step 3 Add the Database container (alwisp_db)

  1. In the Unraid web UI, go to the Docker tab
  2. Click Add Container
  3. Fill in the form:
Field Value
Name alwisp_db
Repository mysql:8.0
Network Type br0
Fixed IP 192.168.1.101 (your reserved DB IP)
Restart Policy Unless Stopped
  1. Scroll to Environment Variables and add:
Key Value
MYSQL_ROOT_PASSWORD (strong root password)
MYSQL_DATABASE alwisp
MYSQL_USER alwisp_user
MYSQL_PASSWORD (strong db password)
  1. Scroll to Path/Volume Mappings and add:
Container Path Host Path Access Mode
/var/lib/mysql /mnt/cache/appdata/alwisp/db_data Read/Write
/docker-entrypoint-initdb.d/init.sql /mnt/cache/appdata/alwisp/docker/mysql/init.sql Read Only
  1. Click Apply — Unraid pulls the MySQL image and starts the container

Step 4 Add the Web container (alwisp_web)

  1. Click Add Container again
  2. Enable Advanced View (toggle in the top-right of the form) so the Extra Parameters field is visible
  3. Fill in the form:
Field Value
Name alwisp_web
Repository alwisp_web:latest (the image you built in Step 2)
Network Type br0
Fixed IP 192.168.1.100 (your reserved web IP)
Restart Policy Unless Stopped
Extra Parameters --pull=never

Why --pull=never? Unraid's Container Builder always tries to pull the repository name from Docker Hub before starting the container. Since alwisp_web is a locally built image that doesn't exist on Docker Hub, the pull fails. --pull=never tells Docker to use the locally built image as-is and skip the pull attempt.

  1. Add Environment Variables:
Key Value
DB_HOST 192.168.1.101 (the DB container's br0 IP from Step 3)
DB_NAME alwisp
DB_USER alwisp_user
DB_PASS (same password set in Step 3)
ADMIN_PASS (password for the staff inbox at /staff-portal)
  1. Add Path/Volume Mappings:
Container Path Host Path Access Mode
/var/www/html /mnt/cache/appdata/alwisp/www Read/Write
/etc/apache2/ssl /mnt/cache/appdata/alwisp/docker/apache/ssl Read Only

The Apache vhost config (000-default.conf) is baked directly into the image via COPY in the Dockerfile — no bind mount needed. To change it, edit the file in docker/apache/ and rebuild the image.

  1. Click Apply

Step 5 Verify containers are running

docker ps --filter name=alwisp

Both alwisp_web and alwisp_db should show status Up.

Navigate to the web container's dedicated IP in a browser:

http://192.168.1.100   → website (replace with your web IP)

Because br0 gives the container its own LAN IP, no port mapping is needed — it behaves like a separate device on your network.


Step 6 Add phpMyAdmin (optional)

For database administration, add a third container:

  1. Click Add Container
  2. Fill in:
Field Value
Name alwisp_pma
Repository phpmyadmin:latest
Network Type br0
Fixed IP 192.168.1.102 (another free LAN IP)
  1. Add Environment Variables:
Key Value
PMA_HOST 192.168.1.101 (DB container's br0 IP)
PMA_USER alwisp_user
PMA_PASSWORD (your db password)
  1. Click Apply, then browse to http://192.168.1.102

Step 7 Point a domain (optional)

Because the web container has a dedicated LAN IP, reverse proxy setup is straightforward:

Using Nginx Proxy Manager on Unraid:

  1. Add a Proxy Host in Nginx Proxy Manager
  2. Forward hostname/IP: 192.168.1.100 (the web container's br0 IP), port 80
  3. Enable SSL via Let's Encrypt
  4. Drop your certificate files into /mnt/cache/appdata/alwisp/docker/apache/ssl/ — they are already mounted into the container

Setting Value
Share path /mnt/cache/appdata/alwisp
Use cache Yes (cache-only or prefer)
Exclude from backup No — include in Appdata backup

Environment Variables

Variable Description
DB_ROOT_PASS MySQL root password — make this strong
DB_NAME Database name (default: alwisp)
DB_USER Application DB user (default: alwisp_user)
DB_PASS Application DB password — make this strong
ADMIN_PASS Password for the staff inbox (/staff-portal) — change before going live

Project Structure

alwisp/
├── docker-compose.yml          # Container orchestration
├── Dockerfile                  # PHP 8.2 + Apache image
├── .env                        # Secrets — never commit
├── .gitignore
├── docker/
│   ├── apache/
│   │   ├── 000-default.conf    # Apache vhost config
│   │   └── ssl/                # TLS certs (gitignored)
│   ├── mysql/
│   │   └── init.sql            # Schema bootstrap on first run
│   └── php/
│       └── php.ini             # PHP runtime settings
└── www/                        # Web root (live-mounted into container)
    ├── index.php               # Front controller / router
    ├── .htaccess               # URL rewriting
    ├── css/style.css           # Design system
    ├── js/main.js              # Nav, counters, scroll reveal
    ├── assets/                 # Logos, images
    ├── includes/
    │   ├── header.php          # Global nav
    │   ├── footer.php          # Global footer
    │   └── db.php              # PDO helper (auto-migrates schema)
    └── pages/
        ├── home.php
        ├── services.php
        ├── coverage.php
        ├── about.php
        ├── contact.php         # Stores submissions in MySQL
        ├── admin-inbox.php     # Staff inbox at /staff-portal (password-gated)
        └── 404.php

Roadmap & Milestones

Milestone 1 — Foundation complete

  • Dockerized LAMP stack (PHP 8.2, Apache, MySQL 8.0)
  • Front-controller PHP router
  • Responsive site skeleton with brand design system
  • Homepage: hero, stats bar, services preview, why-us section, CTA
  • Full services page: Mesh Networking, Managed Services, Structured Cabling, Access Control, IP Cameras
  • Contact form with server-side validation and MySQL storage
  • Staff inbox (/staff-portal) — password-gated, mark read/unread
  • Security headers, OPcache, and Apache hardening
  • Unraid-ready deployment via Docker Compose

Milestone 2 — Content & Branding

  • About page — company story, team bios, mission statement
  • Real contact info (phone, email, service area) wired into footer and contact page
  • Logo finalized and displayed in nav and footer
  • Homepage stats updated to real figures (projects completed, clients, uptime SLA)
  • Email notification on contact form submission (PHPMailer)

Milestone 3 — Portfolio & Project Showcase

  • Portfolio page — completed project write-ups with photos, scope, and outcomes
  • Per-project detail pages (mesh deployment, cabling job, access control install, etc.)
  • Service area section — map or region list showing where ALWISP operates
  • Link portfolio from services page CTAs

Milestone 4 — Client Portal

  • Contact form stores submissions in MySQL
  • Staff inbox — password-gated, mark read/unread
  • Email notification on new submission (PHPMailer)
  • Client account login (PHP sessions)
  • Client dashboard — active projects, network documentation, support requests
  • Quote request tracking — link submitted contact forms to project records
  • Password reset via email token

Milestone 5 — Invoicing & Payments

  • Invoice generation and PDF delivery
  • Online payment via Stripe (one-time project invoices and managed service retainers)
  • Payment history in client dashboard

Milestone 6 — Managed Services Status Page

  • Public status page showing uptime of monitored client networks
  • Admin interface to post and resolve incidents
  • Automated uptime monitoring hook (Uptime Kuma or similar)
  • Email/SMS notification for outages

Milestone 7 — SEO, Performance & Security

  • SSL/TLS configured end-to-end (Let's Encrypt via Nginx Proxy Manager)
  • Sitemap.xml and robots.txt
  • Open Graph meta tags for social sharing
  • Content Security Policy header tuned
  • Security audit

Milestone 8 — Operations & Automation

  • Automated database backups to Unraid share via User Scripts cron
  • Log rotation configured
  • Staging environment (second compose stack on alternate ports)
  • CI/CD pipeline — auto-deploy on push to main via webhook

Updating the Site

Because /mnt/cache/appdata/alwisp/www is bind-mounted directly into alwisp_web, PHP and asset changes take effect immediately — no rebuild or restart needed.

# Pull latest code
cd /mnt/cache/appdata/alwisp
git pull origin main

# If the Dockerfile changed, rebuild the image and restart the web container:
docker build -t alwisp_web:latest /mnt/cache/appdata/alwisp
docker restart alwisp_web

The database container (alwisp_db) is completely independent — updates to the site never affect it.


Useful Commands

# Rebuild the web image after Dockerfile changes
docker build -t alwisp_web:latest /mnt/cache/appdata/alwisp

# Start / stop individual containers
docker start alwisp_web
docker stop alwisp_web
docker restart alwisp_web

docker start alwisp_db
docker stop alwisp_db

# View live logs
docker logs -f alwisp_web
docker logs -f alwisp_db

# Open a shell inside the web container
docker exec -it alwisp_web bash

# Open a MySQL shell (use the DB container's br0 IP if connecting externally)
docker exec -it alwisp_db mysql -u alwisp_user -p alwisp

# Manual database backup
docker exec alwisp_db mysqldump -u alwisp_user -p alwisp > /mnt/cache/appdata/alwisp/backups/backup_$(date +%F).sql

License

Proprietary — Alabama WISP LLC. All rights reserved.