Self-hosted version of the MeshCore Coverage Map, migrated from Cloudflare to Node.js/Express with PostgreSQL.
Only Docker and Docker Compose are required. No Node.js, npm, or PostgreSQL installation needed.
# Update system
sudo apt update
sudo apt upgrade -y
# Install Docker
sudo apt install -y docker.io docker-compose
# Start and enable Docker
sudo systemctl enable docker
sudo systemctl start docker
# Add your user to docker group (to run without sudo)
sudo usermod -aG docker $USER
# Log out and back in for group changes to take effect
exitReconnect via SSH after logging out.
docker --version
docker-compose --version# 1. Clone the repository
git clone <repository-url>
cd meshcore-coverage-map
# 2. Configure environment
cp server/.env.example server/.env
# Edit server/.env with your settings (optional for development)
# 3. Start the application
./docker-compose.sh up --buildThe application will be available at http://localhost:3000
Note: This project uses ./docker-compose.sh instead of docker-compose directly. The helper script automatically loads server/.env for Docker Compose variable substitution, which is required for database credentials and other configuration.
cp server/.env.example server/.env # Edit with your settings if needed
./docker-compose.sh up --buildUseful commands:
./docker-compose.sh up -d --build- Run in background./docker-compose.sh logs -f- View logs./docker-compose.sh down- Stop containers./docker-compose.sh restart- Restart containers
-
Configure environment:
cp server/.env.example server/.env # Edit server/.env with production values -
Stop any existing containers:
./docker-compose.sh -f docker-compose.prod.yml down
-
Start services:
./docker-compose.sh -f docker-compose.prod.yml up -d --build
Edit server/.env (copy from server/.env.example). The server/.env.example file includes:
# Instance Configuration (for running multiple instances on same host)
INSTANCE_NAME=default
HTTP_PORT=3000
HTTPS_PORT=3443
DB_PORT=5432
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=meshmap
DB_USER=meshmap
DB_PASSWORD=your_password
# Server Configuration
PORT=3000
HTTPS_PORT=3443
NODE_ENV=production
# Location validation (optional)
CENTER_POS=37.3382,-121.8863
MAX_DISTANCE_MILES=0 # 0 = no limit
# Inital zoom level of the map view
INITIAL_ZOOM_LEVEL=10
# Automated maintenance
CONSOLIDATE_ENABLED=true
CONSOLIDATE_SCHEDULE=0 2 * * * # Daily at 2 AM
CONSOLIDATE_MAX_AGE_DAYS=14
CLEANUP_ENABLED=true
CLEANUP_SCHEDULE=0 3 * * 0 # Weekly Sunday at 3 AMNote: DB_NAME and DB_USER default to ${INSTANCE_NAME:-meshmap} if not explicitly set, allowing instance-specific databases.
You can run multiple instances (e.g., "west" and "east") on the same host. Docker Compose automatically isolates resources by project name (directory name by default). Each instance needs unique configuration:
Option 1: Separate directories (recommended) Run each instance from a different directory. Docker Compose will automatically prefix volumes and networks with the directory name.
West instance (server/.env in west-instance directory):
INSTANCE_NAME=west
HTTP_PORT=3000
HTTPS_PORT=3443
DB_PORT=5432
DB_NAME=west_meshmap
DB_USER=west_meshmap
DB_PASSWORD=west_passwordEast instance (server/.env in east-instance directory):
INSTANCE_NAME=east
HTTP_PORT=3001
HTTPS_PORT=3444
DB_PORT=5433
DB_NAME=east_meshmap
DB_USER=east_meshmap
DB_PASSWORD=east_passwordOption 2: Same directory, different project names
Use the -p flag to set different project names:
# West instance
./docker-compose.sh -p west up --build
# East instance
./docker-compose.sh -p east up --buildThis ensures:
- Unique container names (
west-meshmap-db,east-meshmap-db, etc.) - Unique ports (no conflicts)
- Separate Docker volumes (prefixed by project name:
west_postgres_data,east_postgres_data) - Separate networks (prefixed by project name:
west_meshmap-network,east_meshmap-network) - Separate databases
For automatic data collection from MQTT feeds:
-
Configure:
cd mqtt-scraper cp config.json.example config.json # Edit config.json with MQTT credentials
-
Start with Docker:
./docker-compose.sh up -d mqtt-scraper ./docker-compose.sh logs -f mqtt-scraper
Configuration example:
{
"mqtt_mode": "public",
"mqtt_host": "mqtt-us-v1.letsmesh.net",
"mqtt_port": 443,
"mqtt_use_websockets": true,
"mqtt_use_tls": true,
"mqtt_username": "YOUR_USERNAME",
"mqtt_password": "YOUR_PASSWORD",
"mqtt_topics": ["meshcore/SFO/+/packets"],
"service_host": "http://app:3000",
"watched_observers": ["OHMC Repeater"]
}GET /get-nodes- Get all coverage, samples, and repeatersGET /get-coverage- Get coverage dataGET /get-samples?p=<prefix>- Get samples (filtered by geohash prefix)GET /get-repeaters- Get all repeatersPOST /put-sample- Add/update a samplePOST /put-repeater- Add/update a repeaterPOST /consolidate?maxAge=<days>- Consolidate old samplesPOST /clean-up?op=repeaters- Clean up stale repeaters
Access the map and tools at:
http://localhost:3000/- Main coverage maphttp://localhost:3000/addSample.html- Add samplehttp://localhost:3000/addRepeater.html- Add repeaterhttp://localhost:3000/wardrive.html- Wardrive app
cd server node scripts/migrate-samples.js
node scripts/migrate-samples.js --source https://source.domain.com/get-samples --dest https://dest.domain.com/put-sample
node scripts/migrate-samples.js --delay 100
Database connection issues:
# For default instance
docker exec default-meshmap-db psql -U meshmap -d meshmap
# For named instance (e.g., "west")
docker exec west-meshmap-db psql -U west_meshmap -d west_meshmapPort already in use:
Change HTTP_PORT in .env or stop the process using the port.
Docker permission denied:
sudo usermod -aG docker $USER
exit # ReconnectDocker Compose "ContainerConfig" error:
./docker-compose.sh -f docker-compose.prod.yml down
./docker-compose.sh -f docker-compose.prod.yml up -d --buildView logs:
# For default instance
./docker-compose.sh logs -f app
./docker-compose.sh logs -f db
./docker-compose.sh logs -f mqtt-scraper
# For named instance, use container names
docker logs -f west-meshmap-app
docker logs -f west-meshmap-dbSee LICENSE file in the root directory.