feat: add utilities + cameras batches (v0.9.11)
Utilities (8 modules): - mealie: recipe manager, port 9925 - actualbudget: personal finance, port 5006 - traccar: GPS tracking, ports 8082 + 5000-5150 device protocols - fmd: Android FindMyDevice server, random admin password, port 8084 - ddclient: dynamic DNS, config template, default start=n - wg-easy: WireGuard VPN+UI, auto-detects public IP, random password - meshcentral: remote device management, hostname prompt, ports 4430+4433 - magicmirror: smart mirror, 1-3 instances, MMM-* module auto-clone Cameras (2 modules): - frigate: AI NVR, auto-enables /dev/dri, starter config.yml, default start=n - frigate-notify: Frigate push alerts, auto-detects local Frigate+ntfy, no web UI https://claude.ai/code/session_01Y4dMKtkqkpvmgDKoRdzhTG
This commit is contained in:
@@ -4,6 +4,41 @@ All notable changes to this project. Versions follow `MAJOR.MINOR.PATCH`.
|
||||
The project is pre-1.0 while the modular system reaches parity with the
|
||||
monolithic `ubuntu-post-install-*.sh` scripts.
|
||||
|
||||
## [0.9.11] - 2026-06-03
|
||||
|
||||
### Added
|
||||
- **Utilities batch** — 8 service modules migrated from the monolith:
|
||||
- `services/mealie.sh` *(utilities)* — Recipe manager & meal planner. PUID/PGID baked;
|
||||
default creds noted (change immediately). Port 9925 → internal 9000.
|
||||
- `services/actualbudget.sh` *(utilities)* — Open-source personal finance (Actual Budget).
|
||||
Minimal container; bank sync via SimpleFIN optional. Port 5006.
|
||||
- `services/traccar.sh` *(utilities)* — GPS tracking server for phones, vehicles, assets.
|
||||
Ships a starter `config/traccar.xml` with H2 embedded DB. Port 8082 + 5000-5150 device
|
||||
protocols (TCP+UDP).
|
||||
- `services/fmd.sh` *(utilities)* — FindMyDevice server for Android. Generates a random
|
||||
admin password; mobile app from F-Droid (not Play Store). Port 8084.
|
||||
- `services/ddclient.sh` *(utilities)* — Dynamic DNS updater; no web UI. Ships a
|
||||
`config/ddclient.conf` template covering Cloudflare, DuckDNS, No-IP. Default start
|
||||
prompt is "n" — edit config first.
|
||||
- `services/wg-easy.sh` *(utilities)* — WireGuard VPN with web UI. Auto-detects public
|
||||
IP for `WG_HOST`; generates random password; requires `NET_ADMIN` + `SYS_MODULE` caps
|
||||
and `ip_forward` sysctl. Ports 51820/udp (VPN) + 51821/tcp (web).
|
||||
- `services/meshcentral.sh` *(utilities)* — Self-hosted remote device management server.
|
||||
Prompts for hostname (domain/IP for agent connections). Ports 4430 (HTTPS) + 4433 (agent).
|
||||
- `services/magicmirror.sh` *(utilities)* — Modular smart mirror / info dashboard.
|
||||
Multi-instance (1-3, ports 8081-8083); each instance in `~/docker/magicmirror/<N>/`.
|
||||
Optionally copies existing `config.js` and auto-clones `MMM-*` third-party modules
|
||||
from GitHub (tries MichMich → bugsounet → MagicMirrorOrg org order).
|
||||
- **Cameras batch** — 2 service modules:
|
||||
- `services/frigate.sh` *(cameras)* — AI-powered NVR with object detection. Auto-enables
|
||||
`/dev/dri/renderD128` for hardware-accelerated detection when present; ships a starter
|
||||
`config/config.yml` with camera examples. `privileged: true` + 1 GB tmpfs cache.
|
||||
Ports 5000 (web), 8554 (RTSP restream), 8555 (WebRTC). Default start prompt is "n" —
|
||||
edit config first.
|
||||
- `services/frigate-notify.sh` *(cameras)* — Push notification sidecar for Frigate events.
|
||||
Auto-detects local Frigate and ntfy installs to pre-fill config defaults. Supports ntfy,
|
||||
Pushover, Discord, Gotify, Telegram, and more. No web UI.
|
||||
|
||||
## [0.9.10] - 2026-06-03
|
||||
|
||||
### Added
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# services/actualbudget.sh — Open-source personal finance / budgeting (Actual Budget).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- ACTUALBUDGET ----).
|
||||
# Own ~/docker/actualbudget/ with a standalone docker-compose.yml.
|
||||
|
||||
register_service actualbudget utilities "Open-source personal finance & budgeting (Actual Budget)" 5006
|
||||
|
||||
install_actualbudget() {
|
||||
require_docker || return 1
|
||||
|
||||
local AB_DIR="$DOCKER_DIR/actualbudget"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Actual Budget would:"
|
||||
echo " - Create $AB_DIR with docker-compose.yml (data/)"
|
||||
echo " - Expose port 5006"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$AB_DIR/data"
|
||||
ensure_docker_dir_ownership "$AB_DIR"
|
||||
cd "$AB_DIR" || return 1
|
||||
|
||||
local TZ_VAL; TZ_VAL=$(cat /etc/timezone 2>/dev/null || echo "UTC")
|
||||
|
||||
cat > docker-compose.yml << 'AB_COMPOSE'
|
||||
name: actualbudget
|
||||
|
||||
services:
|
||||
actualbudget:
|
||||
image: actualbudget/actual-server:latest
|
||||
container_name: actualbudget
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5006:5006"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
env_file:
|
||||
- .env
|
||||
AB_COMPOSE
|
||||
|
||||
cat > .env << AB_ENV
|
||||
TZ=$TZ_VAL
|
||||
AB_ENV
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$AB_DIR"
|
||||
log_success "Actual Budget configured at $AB_DIR"
|
||||
|
||||
configure_caddy_for_service "ActualBudget" "5006" "budget"
|
||||
|
||||
write_readme "$AB_DIR" << MD
|
||||
# Actual Budget
|
||||
|
||||
Open-source personal finance and budgeting tool. Supports bank sync via
|
||||
SimpleFIN (requires a SimpleFIN account at simplefin.org).
|
||||
|
||||
- Web UI: http://localhost:5006
|
||||
- App data: \`data/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $AB_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Notes
|
||||
- First launch: create a budget file or import an existing one.
|
||||
- Bank sync requires a SimpleFIN bridge subscription (simplefin.org).
|
||||
MD
|
||||
|
||||
local START_AB=""
|
||||
prompt_yn "Start Actual Budget now? (y/n):" "y" START_AB
|
||||
if [ "$START_AB" = "y" ] || [ "$START_AB" = "Y" ]; then
|
||||
docker compose up -d && log_success "Actual Budget started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: http://localhost:5006"
|
||||
echo " Bank sync: simplefin.org (optional, paid)"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
# services/ddclient.sh — Dynamic DNS updater (ddclient).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- DDCLIENT ----).
|
||||
# Own ~/docker/ddclient/ with a standalone docker-compose.yml + config.
|
||||
# Supports Cloudflare, DuckDNS, No-IP, and many other providers.
|
||||
# Edit config/ddclient.conf before starting — no web UI.
|
||||
|
||||
register_service ddclient utilities "Dynamic DNS updater — keep your domain pointing at your home IP (ddclient)"
|
||||
|
||||
install_ddclient() {
|
||||
require_docker || return 1
|
||||
|
||||
local DDCLIENT_DIR="$DOCKER_DIR/ddclient"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] ddclient would:"
|
||||
echo " - Create $DDCLIENT_DIR with docker-compose.yml + config/ddclient.conf template"
|
||||
echo " - No web UI — edit config/ddclient.conf for your DNS provider before starting"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$DDCLIENT_DIR"
|
||||
ensure_docker_dir_ownership "$DDCLIENT_DIR"
|
||||
cd "$DDCLIENT_DIR" || return 1
|
||||
|
||||
local TZ_VAL; TZ_VAL=$(cat /etc/timezone 2>/dev/null || echo "UTC")
|
||||
|
||||
cat > docker-compose.yml << 'DDCLIENT_COMPOSE'
|
||||
name: ddclient
|
||||
|
||||
services:
|
||||
ddclient:
|
||||
image: lscr.io/linuxserver/ddclient:latest
|
||||
container_name: ddclient
|
||||
hostname: ddclient
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=${TZ}
|
||||
volumes:
|
||||
- ./config:/config
|
||||
DDCLIENT_COMPOSE
|
||||
|
||||
cat > .env << DDCLIENT_ENV
|
||||
TZ=$TZ_VAL
|
||||
DDCLIENT_ENV
|
||||
|
||||
mkdir -p config
|
||||
|
||||
cat > config/ddclient.conf << 'DDCLIENT_CONF'
|
||||
# ddclient configuration
|
||||
# Docs: https://ddclient.net/
|
||||
#
|
||||
# ⚠️ YOU MUST EDIT THIS FILE before starting ddclient.
|
||||
# Uncomment and fill in the block for your DNS provider.
|
||||
|
||||
daemon=300
|
||||
syslog=yes
|
||||
pid=/var/run/ddclient/ddclient.pid
|
||||
ssl=yes
|
||||
|
||||
# Cloudflare example:
|
||||
# use=web, web=cloudflare
|
||||
# protocol=cloudflare
|
||||
# zone=example.com
|
||||
# login=token
|
||||
# password=your-api-token
|
||||
# example.com
|
||||
|
||||
# DuckDNS example:
|
||||
# use=web
|
||||
# protocol=duckdns
|
||||
# password=your-duckdns-token
|
||||
# yourdomain.duckdns.org
|
||||
|
||||
# No-IP example:
|
||||
# use=web
|
||||
# protocol=noip
|
||||
# login=your@email.com
|
||||
# password=your-password
|
||||
# yourhostname.ddns.net
|
||||
DDCLIENT_CONF
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$DDCLIENT_DIR"
|
||||
log_success "ddclient configured at $DDCLIENT_DIR"
|
||||
|
||||
write_readme "$DDCLIENT_DIR" << MD
|
||||
# ddclient
|
||||
|
||||
Dynamic DNS client — keeps your domain pointing at your home IP address
|
||||
even when your ISP changes it. No web UI; runs as a background daemon.
|
||||
|
||||
- Config: \`config/ddclient.conf\` — **edit before starting**
|
||||
- Supported providers: Cloudflare, DuckDNS, No-IP, FreeDNS, and more
|
||||
|
||||
## Setup
|
||||
1. Edit \`config/ddclient.conf\` for your DNS provider.
|
||||
2. Start: \`docker compose up -d\`
|
||||
3. Check logs: \`docker compose logs -f\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $DDCLIENT_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Docs
|
||||
- https://ddclient.net/
|
||||
- Cloudflare setup: https://ddclient.net/protocols/cloudflare.html
|
||||
MD
|
||||
|
||||
echo ""
|
||||
log_warning "Edit config/ddclient.conf for your DNS provider before starting."
|
||||
echo ""
|
||||
local START_DDC=""
|
||||
prompt_yn "Start ddclient now? (y/n):" "n" START_DDC
|
||||
if [ "$START_DDC" = "y" ] || [ "$START_DDC" = "Y" ]; then
|
||||
docker compose up -d && log_success "ddclient started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Config: $DDCLIENT_DIR/config/ddclient.conf"
|
||||
echo " Docs: https://ddclient.net/"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
# services/fmd.sh — FindMyDevice server for Android device tracking (FMD).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- FINDMYDEVICE ----).
|
||||
# Own ~/docker/fmd/ with a standalone docker-compose.yml + .env.
|
||||
# Mobile app: "FindMyDevice" on F-Droid — not the Play Store version.
|
||||
|
||||
register_service fmd utilities "Android device tracking — alternative to Google Find My Device (FMD)" 8084
|
||||
|
||||
install_fmd() {
|
||||
require_docker || return 1
|
||||
|
||||
local FMD_DIR="$DOCKER_DIR/fmd"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] FindMyDevice would:"
|
||||
echo " - Create $FMD_DIR with docker-compose.yml + .env (data/)"
|
||||
echo " - Generate a random admin password"
|
||||
echo " - Expose port 8084"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$FMD_DIR"
|
||||
ensure_docker_dir_ownership "$FMD_DIR"
|
||||
cd "$FMD_DIR" || return 1
|
||||
|
||||
local FMD_PASS
|
||||
FMD_PASS=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
||||
|
||||
cat > docker-compose.yml << 'FMD_COMPOSE'
|
||||
name: fmd
|
||||
|
||||
services:
|
||||
fmd:
|
||||
image: nulide/findmydevice
|
||||
container_name: fmd
|
||||
hostname: fmd
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- FMD_ADMIN_PASSWORD=${FMD_ADMIN_PASSWORD}
|
||||
volumes:
|
||||
- ./data:/fmd/data
|
||||
ports:
|
||||
- "8084:8080"
|
||||
FMD_COMPOSE
|
||||
|
||||
cat > .env << FMD_ENV
|
||||
FMD_ADMIN_PASSWORD=$FMD_PASS
|
||||
FMD_ENV
|
||||
|
||||
mkdir -p data
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$FMD_DIR"
|
||||
log_success "FindMyDevice configured at $FMD_DIR"
|
||||
|
||||
configure_caddy_for_service "FindMyDevice" "8084" "fmd"
|
||||
|
||||
write_readme "$FMD_DIR" << MD
|
||||
# FindMyDevice (FMD)
|
||||
|
||||
Self-hosted Android device tracking — locate, lock, or wipe your device
|
||||
from the web UI. Alternative to Google's Find My Device.
|
||||
|
||||
- Web UI: http://localhost:8084
|
||||
- Admin password: stored in \`.env\` (\`FMD_ADMIN_PASSWORD\`)
|
||||
- App data: \`data/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $FMD_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Mobile app
|
||||
Install **FindMyDevice** from **F-Droid** (not the Play Store version):
|
||||
1. Open the app → Settings → Server URL → \`http://YOUR-SERVER-IP:8084\`
|
||||
2. Enter your admin password from \`.env\`
|
||||
3. Grant location and accessibility permissions
|
||||
MD
|
||||
|
||||
local START_FMD=""
|
||||
prompt_yn "Start FindMyDevice now? (y/n):" "y" START_FMD
|
||||
if [ "$START_FMD" = "y" ] || [ "$START_FMD" = "Y" ]; then
|
||||
docker compose up -d && log_success "FindMyDevice started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: http://localhost:8084"
|
||||
echo " Password: $FMD_PASS (saved in .env)"
|
||||
echo " Mobile app: FindMyDevice on F-Droid"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
# services/frigate-notify.sh — Push notification sidecar for Frigate events.
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- FRIGATE-NOTIFY ----).
|
||||
# Own ~/docker/frigate-notify/ with a standalone docker-compose.yml + config.yml.
|
||||
# Supports ntfy, Pushover, Discord, Gotify, Telegram, and more. No web UI.
|
||||
# Auto-detects local Frigate and ntfy installs to pre-fill config defaults.
|
||||
|
||||
register_service frigate-notify cameras "Push alerts for Frigate detection events (Frigate-Notify)"
|
||||
|
||||
install_frigate-notify() {
|
||||
require_docker || return 1
|
||||
|
||||
local FN_DIR="$DOCKER_DIR/frigate-notify"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Frigate-Notify would:"
|
||||
echo " - Create $FN_DIR with docker-compose.yml + config.yml"
|
||||
echo " - Auto-detect local Frigate and ntfy installs"
|
||||
echo " - No web UI — configure via config.yml"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$FN_DIR"
|
||||
ensure_docker_dir_ownership "$FN_DIR"
|
||||
cd "$FN_DIR" || return 1
|
||||
|
||||
cat > docker-compose.yml << 'FN_COMPOSE'
|
||||
name: frigate-notify
|
||||
|
||||
services:
|
||||
frigate-notify:
|
||||
image: ghcr.io/0x2142/frigate-notify:latest
|
||||
container_name: frigate-notify
|
||||
hostname: frigate-notify
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config.yml:/app/config.yml:ro
|
||||
FN_COMPOSE
|
||||
|
||||
# Smart defaults based on what's installed
|
||||
local FRIGATE_URL="http://frigate:5000"
|
||||
local NTFY_URL="https://ntfy.sh"
|
||||
local NTFY_TOPIC="frigate-alerts"
|
||||
|
||||
if [ -d "$DOCKER_DIR/frigate" ]; then
|
||||
log_success "Local Frigate detected — using http://frigate:5000"
|
||||
else
|
||||
log_warning "Frigate not found locally — using default URL (update config.yml if needed)"
|
||||
fi
|
||||
|
||||
if [ -d "$DOCKER_DIR/ntfy" ]; then
|
||||
NTFY_URL="http://ntfy:80"
|
||||
log_success "Local ntfy detected — using http://ntfy:80"
|
||||
else
|
||||
log_warning "Local ntfy not found — using ntfy.sh (update config.yml for self-hosted)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
prompt_text "Frigate URL [$FRIGATE_URL]:" "$FRIGATE_URL" FRIGATE_URL
|
||||
prompt_text "ntfy server URL [$NTFY_URL]:" "$NTFY_URL" NTFY_URL
|
||||
prompt_text "ntfy topic [frigate-alerts]:" "frigate-alerts" NTFY_TOPIC
|
||||
|
||||
cat > config.yml << FN_CONFIG
|
||||
# Frigate-Notify Configuration
|
||||
# Docs: https://frigate-notify.0x2142.com
|
||||
#
|
||||
# Edit this file if notifications don't arrive — check Frigate URL,
|
||||
# ntfy server, and that containers share a Docker network.
|
||||
|
||||
frigate:
|
||||
server: $FRIGATE_URL
|
||||
webapi:
|
||||
enabled: true
|
||||
interval: 30
|
||||
|
||||
alerts:
|
||||
general:
|
||||
send_startup_message: true
|
||||
labels:
|
||||
- person
|
||||
- car
|
||||
# - dog
|
||||
# - package
|
||||
|
||||
notifiers:
|
||||
- name: ntfy
|
||||
enabled: true
|
||||
provider: ntfy
|
||||
config:
|
||||
server: $NTFY_URL
|
||||
topic: $NTFY_TOPIC
|
||||
FN_CONFIG
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$FN_DIR"
|
||||
log_success "Frigate-Notify configured at $FN_DIR"
|
||||
|
||||
write_readme "$FN_DIR" << MD
|
||||
# Frigate-Notify
|
||||
|
||||
Push notification sidecar for Frigate — sends alerts when Frigate detects
|
||||
people, cars, animals, or custom objects. Supports ntfy, Pushover, Discord,
|
||||
Gotify, Telegram, and more. No web UI.
|
||||
|
||||
- Config: \`config.yml\` — edit notification targets here
|
||||
- Frigate events polled every 30 seconds by default
|
||||
- Docs: https://frigate-notify.0x2142.com
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $FN_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # check for delivery errors
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Adding more notifiers
|
||||
Edit \`config.yml\` and add entries under \`notifiers:\`. Supported providers:
|
||||
ntfy, Pushover, Discord (webhook), Gotify, Telegram, SMTP, and more.
|
||||
See: https://frigate-notify.0x2142.com/configuration/alerts/
|
||||
MD
|
||||
|
||||
local START_FN=""
|
||||
prompt_yn "Start Frigate-Notify now? (y/n):" "y" START_FN
|
||||
if [ "$START_FN" = "y" ] || [ "$START_FN" = "Y" ]; then
|
||||
docker compose up -d && log_success "Frigate-Notify started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Config: $FN_DIR/config.yml"
|
||||
echo " Docs: https://frigate-notify.0x2142.com"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/bin/bash
|
||||
# services/frigate.sh — AI-powered NVR for security cameras (Frigate).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- FRIGATE NVR ----).
|
||||
# Own ~/docker/frigate/ with a standalone docker-compose.yml + .env + config.yml.
|
||||
# Auto-enables /dev/dri/renderD128 for hardware detection (Intel/AMD) when present.
|
||||
# YOU MUST edit config/config.yml to add your camera RTSP streams before starting.
|
||||
|
||||
register_service frigate cameras "AI-powered NVR — object detection on security cameras (Frigate)" 5000
|
||||
|
||||
install_frigate() {
|
||||
require_docker || return 1
|
||||
|
||||
local FRIGATE_DIR="$DOCKER_DIR/frigate"
|
||||
local DEFAULT_MEDIA="$ACTUAL_HOME/frigate"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Frigate would:"
|
||||
echo " - Create $FRIGATE_DIR with docker-compose.yml + .env + config/config.yml"
|
||||
echo " - Auto-enable /dev/dri/renderD128 for GPU-assisted detection if present"
|
||||
echo " - Expose ports 5000 (web), 8554 (RTSP restream), 8555 (WebRTC)"
|
||||
echo " - Write a starter config.yml — edit to add camera streams before starting"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local FRIGATE_MEDIA=""
|
||||
prompt_text "Path for recordings/snapshots [$DEFAULT_MEDIA]:" "$DEFAULT_MEDIA" FRIGATE_MEDIA
|
||||
FRIGATE_MEDIA="${FRIGATE_MEDIA/#\~/$ACTUAL_HOME}"; FRIGATE_MEDIA="${FRIGATE_MEDIA%/}"
|
||||
|
||||
mkdir -p "$FRIGATE_DIR"
|
||||
ensure_docker_dir_ownership "$FRIGATE_DIR"
|
||||
cd "$FRIGATE_DIR" || return 1
|
||||
|
||||
local TZ_VAL; TZ_VAL=$(cat /etc/timezone 2>/dev/null || echo "UTC")
|
||||
|
||||
# Hardware detection: include /dev/dri only when a render node exists
|
||||
local DEVICE_BLOCK=""
|
||||
if [ -e /dev/dri/renderD128 ]; then
|
||||
DEVICE_BLOCK=" devices:
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128"
|
||||
log_success "Render node found — enabling hardware-accelerated detection"
|
||||
else
|
||||
log_warning "No /dev/dri/renderD128 — Frigate will use CPU detection."
|
||||
fi
|
||||
|
||||
cat > docker-compose.yml << FRIGATE_COMPOSE
|
||||
name: frigate
|
||||
|
||||
services:
|
||||
frigate:
|
||||
image: ghcr.io/blakeblackshear/frigate:stable
|
||||
container_name: frigate
|
||||
hostname: frigate
|
||||
restart: unless-stopped
|
||||
privileged: true
|
||||
shm_size: "256mb"
|
||||
environment:
|
||||
- TZ=$TZ_VAL
|
||||
$DEVICE_BLOCK
|
||||
volumes:
|
||||
- ./config:/config
|
||||
- \${FRIGATE_MEDIA}:/media/frigate
|
||||
- type: tmpfs
|
||||
target: /tmp/cache
|
||||
tmpfs:
|
||||
size: 1000000000
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- "8554:8554"
|
||||
- "8555:8555/tcp"
|
||||
- "8555:8555/udp"
|
||||
FRIGATE_COMPOSE
|
||||
|
||||
cat > .env << FRIGATE_ENV
|
||||
FRIGATE_MEDIA=$FRIGATE_MEDIA
|
||||
FRIGATE_ENV
|
||||
|
||||
mkdir -p config
|
||||
mkdir -p "$FRIGATE_MEDIA"
|
||||
|
||||
cat > config/config.yml << 'FRIGATE_CONFIG'
|
||||
# Frigate Configuration — Docs: https://docs.frigate.video
|
||||
#
|
||||
# ⚠️ YOU MUST EDIT THIS FILE to add your cameras before starting Frigate.
|
||||
|
||||
mqtt:
|
||||
enabled: false # Set to true and configure if you use Home Assistant
|
||||
|
||||
cameras:
|
||||
# Example — replace with your camera details:
|
||||
# front_door:
|
||||
# ffmpeg:
|
||||
# inputs:
|
||||
# - path: rtsp://user:pass@192.168.1.100:554/stream
|
||||
# roles: [detect, record]
|
||||
# detect:
|
||||
# width: 1280
|
||||
# height: 720
|
||||
# fps: 5
|
||||
|
||||
detectors:
|
||||
default:
|
||||
type: cpu # Change to 'edgetpu' for Coral TPU or 'openvino' for Intel GPU
|
||||
|
||||
record:
|
||||
enabled: true
|
||||
retain:
|
||||
days: 7
|
||||
mode: motion
|
||||
|
||||
snapshots:
|
||||
enabled: true
|
||||
retain:
|
||||
default: 7
|
||||
FRIGATE_CONFIG
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$FRIGATE_DIR"
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$FRIGATE_MEDIA" 2>/dev/null || true
|
||||
log_success "Frigate configured at $FRIGATE_DIR"
|
||||
|
||||
configure_caddy_for_service "Frigate" "5000" "frigate"
|
||||
|
||||
write_readme "$FRIGATE_DIR" << MD
|
||||
# Frigate NVR
|
||||
|
||||
AI-powered network video recorder with real-time object detection for
|
||||
security cameras. Detects people, cars, animals, and more.
|
||||
|
||||
- Web UI: http://localhost:5000
|
||||
- RTSP restream: port 8554
|
||||
- WebRTC: port 8555
|
||||
- Recordings: \`$FRIGATE_MEDIA\`
|
||||
- Config: \`config/config.yml\` — **add your camera RTSP streams here**
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $FRIGATE_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## First steps
|
||||
1. Edit \`config/config.yml\` — add your camera RTSP URLs under \`cameras:\`
|
||||
2. Start Frigate: \`docker compose up -d\`
|
||||
3. Open http://localhost:5000 to view cameras and configure detection zones
|
||||
|
||||
## Hardware acceleration
|
||||
- Intel/AMD GPU: uncomment the \`devices: [/dev/dri/renderD128]\` block
|
||||
- Google Coral TPU: set \`detectors.default.type: edgetpu\` + add USB device
|
||||
- Docs: https://docs.frigate.video/configuration/hardware_acceleration
|
||||
MD
|
||||
|
||||
echo ""
|
||||
log_warning "Edit config/config.yml to add your camera RTSP streams before starting."
|
||||
echo ""
|
||||
local START_FRIGATE=""
|
||||
prompt_yn "Start Frigate now anyway? (y/n):" "n" START_FRIGATE
|
||||
if [ "$START_FRIGATE" = "y" ] || [ "$START_FRIGATE" = "Y" ]; then
|
||||
docker compose up -d && log_success "Frigate started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: http://localhost:5000"
|
||||
echo " Config: $FRIGATE_DIR/config/config.yml (add cameras here)"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
# services/magicmirror.sh — Modular smart mirror / info dashboard (MagicMirror²).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- MAGIC MIRROR ----).
|
||||
# Supports 1-3 instances (ports 8081-8083) each in ~/docker/magicmirror/<N>/.
|
||||
# If you provide an existing config.js, third-party MMM-* modules are detected
|
||||
# and cloned from GitHub automatically.
|
||||
|
||||
register_service magicmirror utilities "Modular smart mirror / info dashboard (MagicMirror²)" 8081
|
||||
|
||||
install_magicmirror() {
|
||||
require_docker || return 1
|
||||
|
||||
local MM_BASE="$DOCKER_DIR/magicmirror"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] MagicMirror would:"
|
||||
echo " - Ask how many instances (1-3, ports 8081-8083)"
|
||||
echo " - Create $MM_BASE/<N>/ for each instance"
|
||||
echo " - Optionally copy your existing config.js + clone MMM-* modules"
|
||||
echo " - Offer a Caddy reverse proxy (first instance) and to start"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Number of instances
|
||||
local MM_COUNT=""
|
||||
prompt_text "How many MagicMirror instances? [1-3, default: 1]:" "1" MM_COUNT
|
||||
MM_COUNT="${MM_COUNT:-1}"
|
||||
[ "$MM_COUNT" -gt 3 ] 2>/dev/null && MM_COUNT=3
|
||||
[ "$MM_COUNT" -lt 1 ] 2>/dev/null && MM_COUNT=1
|
||||
|
||||
mkdir -p "$MM_BASE"
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$MM_BASE"
|
||||
|
||||
local TZ_VAL; TZ_VAL=$(cat /etc/timezone 2>/dev/null || echo "UTC")
|
||||
local i MM_PORT MM_DIR
|
||||
|
||||
for i in $(seq 1 "$MM_COUNT"); do
|
||||
MM_PORT=$((8080 + i))
|
||||
MM_DIR="$MM_BASE/$i"
|
||||
|
||||
echo ""
|
||||
echo "── Instance $i (port $MM_PORT) ──"
|
||||
mkdir -p "$MM_DIR"
|
||||
ensure_docker_dir_ownership "$MM_DIR"
|
||||
cd "$MM_DIR" || continue
|
||||
|
||||
cat > docker-compose.yml << MM_COMPOSE
|
||||
name: mm-$MM_PORT
|
||||
|
||||
services:
|
||||
magicmirror:
|
||||
image: karsten13/magicmirror:latest
|
||||
container_name: magicmirror-$MM_PORT
|
||||
hostname: magicmirror-$MM_PORT
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=$TZ_VAL
|
||||
volumes:
|
||||
- ./config:/opt/magic_mirror/config
|
||||
- ./modules:/opt/magic_mirror/modules
|
||||
- ./css:/opt/magic_mirror/css
|
||||
ports:
|
||||
- "$MM_PORT:8080"
|
||||
MM_COMPOSE
|
||||
|
||||
mkdir -p config modules css
|
||||
|
||||
# Offer to copy an existing config.js
|
||||
local MM_CONFIG_CHOICE=""
|
||||
echo ""
|
||||
echo " Config options:"
|
||||
echo " [1] Use default config (basic built-in modules)"
|
||||
echo " [2] Copy existing config.js from a path"
|
||||
if [ "$UNATTENDED" = true ]; then
|
||||
MM_CONFIG_CHOICE="1"
|
||||
else
|
||||
read -r -p " Choose [1]: " MM_CONFIG_CHOICE
|
||||
MM_CONFIG_CHOICE="${MM_CONFIG_CHOICE:-1}"
|
||||
fi
|
||||
|
||||
if [ "$MM_CONFIG_CHOICE" = "2" ]; then
|
||||
local MM_CONFIG_PATH=""
|
||||
read -r -p " Path to config.js: " MM_CONFIG_PATH
|
||||
if [ -f "$MM_CONFIG_PATH" ]; then
|
||||
cp "$MM_CONFIG_PATH" config/config.js
|
||||
log_success "Copied config from $MM_CONFIG_PATH"
|
||||
|
||||
# Copy custom.css if it exists next to config.js
|
||||
local MM_CSS_DIR="${MM_CONFIG_PATH%/*}"
|
||||
[ -f "$MM_CSS_DIR/custom.css" ] && cp "$MM_CSS_DIR/custom.css" css/custom.css && log_success "Copied custom.css"
|
||||
|
||||
# Detect MMM-* third-party modules referenced in config
|
||||
local THIRD_PARTY_MODS
|
||||
THIRD_PARTY_MODS=$(grep -oP "module:\s*[\"']MMM-[^\"']+[\"']" config/config.js 2>/dev/null \
|
||||
| sed "s/module:\s*[\"']//g" | sed "s/[\"']//g" | sort -u)
|
||||
|
||||
if [ -n "$THIRD_PARTY_MODS" ]; then
|
||||
echo ""
|
||||
echo " Third-party modules found in config:"
|
||||
echo "$THIRD_PARTY_MODS" | while read -r mod; do echo " - $mod"; done
|
||||
echo ""
|
||||
local MM_DL_MODS=""
|
||||
prompt_yn " Download these modules from GitHub? (y/n):" "y" MM_DL_MODS
|
||||
if [ "$MM_DL_MODS" = "y" ] || [ "$MM_DL_MODS" = "Y" ]; then
|
||||
cd modules || true
|
||||
echo "$THIRD_PARTY_MODS" | while read -r mod; do
|
||||
[ -z "$mod" ] || [ -d "$mod" ] && continue
|
||||
echo " Downloading $mod..."
|
||||
git clone --depth 1 "https://github.com/MichMich/${mod}.git" 2>/dev/null || \
|
||||
git clone --depth 1 "https://github.com/bugsounet/${mod}.git" 2>/dev/null || \
|
||||
git clone --depth 1 "https://github.com/MagicMirrorOrg/${mod}.git" 2>/dev/null || \
|
||||
log_warning "Could not find $mod — search at https://github.com/topics/magicmirror"
|
||||
done
|
||||
cd "$MM_DIR" || true
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warning "File not found: $MM_CONFIG_PATH — using default config"
|
||||
fi
|
||||
fi
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$MM_DIR"
|
||||
log_success "MagicMirror instance $i configured at $MM_DIR (port $MM_PORT)"
|
||||
|
||||
# Offer Caddy only for first instance
|
||||
[ "$i" -eq 1 ] && configure_caddy_for_service "MagicMirror" "$MM_PORT" "mirror"
|
||||
|
||||
local START_MM=""
|
||||
prompt_yn "Start instance $i now? (y/n):" "y" START_MM
|
||||
if [ "$START_MM" = "y" ] || [ "$START_MM" = "Y" ]; then
|
||||
docker compose up -d && log_success "MagicMirror instance $i started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo " Access at: http://localhost:$MM_PORT"
|
||||
done
|
||||
|
||||
write_readme "$MM_BASE" << MD
|
||||
# MagicMirror²
|
||||
|
||||
Modular smart mirror / info dashboard. Each instance has its own port and
|
||||
independent config, modules, and CSS.
|
||||
|
||||
| Instance | Port | Directory |
|
||||
|----------|------|-----------|
|
||||
$(for j in $(seq 1 "$MM_COUNT"); do echo "| $j | $((8080 + j)) | \`$MM_BASE/$j/\` |"; done)
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $MM_BASE/1
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Config
|
||||
- Edit \`<instance>/config/config.js\` for layout and module settings.
|
||||
- Add CSS overrides in \`<instance>/css/custom.css\`.
|
||||
- Third-party modules go in \`<instance>/modules/<module-name>/\`.
|
||||
Then run \`docker exec magicmirror-PORT sh -c 'cd /opt/magic_mirror/modules/<name> && npm install --production'\`
|
||||
|
||||
## Finding modules
|
||||
Browse: https://github.com/topics/magicmirror
|
||||
MD
|
||||
|
||||
echo ""
|
||||
echo " MagicMirror config: $MM_BASE/<instance>/config/config.js"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# services/mealie.sh — Recipe manager & meal planner (Mealie).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- MEALIE ----).
|
||||
# Own ~/docker/mealie/ with a standalone docker-compose.yml.
|
||||
|
||||
register_service mealie utilities "Recipe manager & meal planner (Mealie)" 9925
|
||||
|
||||
install_mealie() {
|
||||
require_docker || return 1
|
||||
|
||||
local MEALIE_DIR="$DOCKER_DIR/mealie"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Mealie would:"
|
||||
echo " - Create $MEALIE_DIR with docker-compose.yml (data/)"
|
||||
echo " - Expose port 9925"
|
||||
echo " - Default login: changeme@email.com / MyPassword (change immediately)"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$MEALIE_DIR"
|
||||
ensure_docker_dir_ownership "$MEALIE_DIR"
|
||||
cd "$MEALIE_DIR" || return 1
|
||||
|
||||
local TZ_VAL UID_VAL GID_VAL
|
||||
TZ_VAL=$(cat /etc/timezone 2>/dev/null || echo "UTC")
|
||||
UID_VAL=$(id -u "$ACTUAL_USER"); GID_VAL=$(id -g "$ACTUAL_USER")
|
||||
|
||||
cat > docker-compose.yml << MEALIE_COMPOSE
|
||||
name: mealie
|
||||
|
||||
services:
|
||||
mealie:
|
||||
image: ghcr.io/mealie-recipes/mealie:latest
|
||||
container_name: mealie
|
||||
hostname: mealie
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=$UID_VAL
|
||||
- PGID=$GID_VAL
|
||||
- TZ=$TZ_VAL
|
||||
- ALLOW_SIGNUP=true
|
||||
- MAX_WORKERS=1
|
||||
- WEB_CONCURRENCY=1
|
||||
- BASE_URL=http://localhost:9925
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
ports:
|
||||
- "9925:9000"
|
||||
MEALIE_COMPOSE
|
||||
|
||||
mkdir -p data
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$MEALIE_DIR"
|
||||
log_success "Mealie configured at $MEALIE_DIR"
|
||||
|
||||
configure_caddy_for_service "Mealie" "9925" "recipes"
|
||||
|
||||
write_readme "$MEALIE_DIR" << MD
|
||||
# Mealie
|
||||
|
||||
Recipe manager and meal planner — import recipes from any URL, plan meals,
|
||||
and generate shopping lists. Optional AI-powered recipe parsing.
|
||||
|
||||
- Web UI: http://localhost:9925
|
||||
- Default login: changeme@email.com / MyPassword (change immediately!)
|
||||
- App data: \`data/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $MEALIE_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Notes
|
||||
- If using Caddy, update \`BASE_URL\` in \`docker-compose.yml\` to your domain.
|
||||
MD
|
||||
|
||||
local START_MEALIE=""
|
||||
prompt_yn "Start Mealie now? (y/n):" "y" START_MEALIE
|
||||
if [ "$START_MEALIE" = "y" ] || [ "$START_MEALIE" = "Y" ]; then
|
||||
docker compose up -d && log_success "Mealie started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: http://localhost:9925"
|
||||
echo " Default: changeme@email.com / MyPassword (change immediately!)"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
# services/meshcentral.sh — Self-hosted remote device management server (MeshCentral).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- MESHCENTRAL SERVER ----).
|
||||
# Own ~/docker/meshcentral/ with a standalone docker-compose.yml + .env.
|
||||
# HTTPS on port 4430, agent listener on 4433. First visit: create admin account.
|
||||
|
||||
register_service meshcentral utilities "Self-hosted remote device management server (MeshCentral)" 4430
|
||||
|
||||
install_meshcentral() {
|
||||
require_docker || return 1
|
||||
|
||||
local MC_DIR="$DOCKER_DIR/meshcentral"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] MeshCentral would:"
|
||||
echo " - Create $MC_DIR with docker-compose.yml + .env (data/ files/ backups/)"
|
||||
echo " - Prompt for hostname (domain or IP for agent connections)"
|
||||
echo " - Expose port 4430 (HTTPS web) and 4433 (agent)"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local MC_HOSTNAME=""
|
||||
prompt_text "MeshCentral hostname (domain or IP) [localhost]:" "localhost" MC_HOSTNAME
|
||||
MC_HOSTNAME="${MC_HOSTNAME:-localhost}"
|
||||
|
||||
mkdir -p "$MC_DIR"
|
||||
ensure_docker_dir_ownership "$MC_DIR"
|
||||
cd "$MC_DIR" || return 1
|
||||
|
||||
cat > docker-compose.yml << 'MC_COMPOSE'
|
||||
name: meshcentral
|
||||
|
||||
services:
|
||||
meshcentral:
|
||||
image: ghcr.io/ylianst/meshcentral:latest
|
||||
container_name: meshcentral
|
||||
hostname: meshcentral
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- HOSTNAME=${MC_HOSTNAME:-localhost}
|
||||
- REVERSE_PROXY=${MC_REVERSE_PROXY:-false}
|
||||
- REVERSE_PROXY_TLS_PORT=${MC_TLS_PORT:-443}
|
||||
- IFRAME=false
|
||||
- ALLOW_NEW_ACCOUNTS=true
|
||||
- WEBRTC=true
|
||||
volumes:
|
||||
- ./data:/opt/meshcentral/meshcentral-data
|
||||
- ./files:/opt/meshcentral/meshcentral-files
|
||||
- ./backups:/opt/meshcentral/meshcentral-backups
|
||||
ports:
|
||||
- "4430:443"
|
||||
- "4433:4433"
|
||||
MC_COMPOSE
|
||||
|
||||
cat > .env << MC_ENV
|
||||
MC_HOSTNAME=$MC_HOSTNAME
|
||||
MC_REVERSE_PROXY=false
|
||||
MC_TLS_PORT=443
|
||||
MC_ENV
|
||||
|
||||
mkdir -p data files backups
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$MC_DIR"
|
||||
log_success "MeshCentral configured at $MC_DIR"
|
||||
|
||||
configure_caddy_for_service "MeshCentral" "4430" "mesh"
|
||||
|
||||
write_readme "$MC_DIR" << MD
|
||||
# MeshCentral
|
||||
|
||||
Self-hosted remote device management — remotely access, manage, and monitor
|
||||
all your computers from a single web interface. Install agents on each device.
|
||||
|
||||
- Web UI: https://localhost:4430 (self-signed cert on first launch)
|
||||
- Agent listener: port 4433 (devices connect here — forward this port if remote)
|
||||
- Hostname: \`$MC_HOSTNAME\` (update \`MC_HOSTNAME\` in .env if it changes)
|
||||
- App data: \`data/\`, \`files/\`, \`backups/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $MC_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## First launch
|
||||
1. Open https://localhost:4430 (accept the self-signed cert warning)
|
||||
2. Create your admin account
|
||||
3. Go to "My Devices" → "+ Add Device" → download the agent for each OS
|
||||
4. Install the agent on every computer you want to manage
|
||||
|
||||
## Remote access
|
||||
For devices outside your LAN to connect:
|
||||
- Forward **TCP port 4433** on your router to this server
|
||||
- Set \`MC_HOSTNAME\` in \`.env\` to your public domain/IP, then restart
|
||||
|
||||
## Docs
|
||||
https://meshcentral.com/docs/
|
||||
MD
|
||||
|
||||
local START_MC=""
|
||||
prompt_yn "Start MeshCentral now? (y/n):" "y" START_MC
|
||||
if [ "$START_MC" = "y" ] || [ "$START_MC" = "Y" ]; then
|
||||
docker compose up -d && log_success "MeshCentral started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: https://localhost:4430 (accept self-signed cert)"
|
||||
echo " First visit: create your admin account"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
# services/traccar.sh — GPS tracking server (Traccar).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- TRACCAR ----).
|
||||
# Own ~/docker/traccar/ with a standalone docker-compose.yml + config XML.
|
||||
|
||||
register_service traccar utilities "GPS tracking server — phones, vehicles, assets (Traccar)" 8082
|
||||
|
||||
install_traccar() {
|
||||
require_docker || return 1
|
||||
|
||||
local TRACCAR_DIR="$DOCKER_DIR/traccar"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Traccar would:"
|
||||
echo " - Create $TRACCAR_DIR with docker-compose.yml + config/traccar.xml"
|
||||
echo " - Expose port 8082 (web) and 5000-5150 (device protocols)"
|
||||
echo " - Default login: admin@admin.com / admin (change immediately!)"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$TRACCAR_DIR"
|
||||
ensure_docker_dir_ownership "$TRACCAR_DIR"
|
||||
cd "$TRACCAR_DIR" || return 1
|
||||
|
||||
cat > docker-compose.yml << 'TRACCAR_COMPOSE'
|
||||
name: traccar
|
||||
|
||||
services:
|
||||
traccar:
|
||||
image: traccar/traccar:latest
|
||||
container_name: traccar
|
||||
hostname: traccar
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./logs:/opt/traccar/logs:rw
|
||||
- ./data:/opt/traccar/data:rw
|
||||
- ./config/traccar.xml:/opt/traccar/conf/traccar.xml:ro
|
||||
ports:
|
||||
- "8082:8082"
|
||||
- "5000-5150:5000-5150"
|
||||
- "5000-5150:5000-5150/udp"
|
||||
TRACCAR_COMPOSE
|
||||
|
||||
mkdir -p logs data config
|
||||
|
||||
cat > config/traccar.xml << 'TRACCAR_XML'
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'>
|
||||
|
||||
<properties>
|
||||
<entry key='config.default'>./conf/default.xml</entry>
|
||||
<entry key='database.driver'>org.h2.Driver</entry>
|
||||
<entry key='database.url'>jdbc:h2:/opt/traccar/data/database</entry>
|
||||
<entry key='database.user'>sa</entry>
|
||||
<entry key='database.password'></entry>
|
||||
</properties>
|
||||
TRACCAR_XML
|
||||
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$TRACCAR_DIR"
|
||||
log_success "Traccar configured at $TRACCAR_DIR"
|
||||
|
||||
configure_caddy_for_service "Traccar" "8082" "traccar"
|
||||
|
||||
write_readme "$TRACCAR_DIR" << MD
|
||||
# Traccar
|
||||
|
||||
GPS tracking server. Track phones, vehicles, and assets via the Traccar
|
||||
Android/iOS app, OwnTracks, or any of 200+ supported device protocols.
|
||||
|
||||
- Web UI: http://localhost:8082
|
||||
- Default login: admin@admin.com / admin (change immediately!)
|
||||
- Device protocols: ports 5000-5150 (TCP + UDP)
|
||||
- Config: \`config/traccar.xml\`
|
||||
- App data: \`data/\` and \`logs/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $TRACCAR_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Mobile apps
|
||||
- Traccar Client (Android/iOS): set server to \`http://YOUR-IP:8082\`
|
||||
- OwnTracks (Android/iOS): configure HTTP endpoint to Traccar
|
||||
MD
|
||||
|
||||
local START_TRACCAR=""
|
||||
prompt_yn "Start Traccar now? (y/n):" "y" START_TRACCAR
|
||||
if [ "$START_TRACCAR" = "y" ] || [ "$START_TRACCAR" = "Y" ]; then
|
||||
docker compose up -d && log_success "Traccar started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Access at: http://localhost:8082"
|
||||
echo " Default: admin@admin.com / admin (change immediately!)"
|
||||
echo ""
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
# services/wg-easy.sh — WireGuard VPN with a web management UI (wg-easy).
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
#
|
||||
# Ported from ubuntu-post-install-24.04-crowdsec.sh (# ---- WG-EASY ----).
|
||||
# Own ~/docker/wg-easy/ with a standalone docker-compose.yml + .env.
|
||||
# Requires cap_add: NET_ADMIN + SYS_MODULE and ip_forward sysctl.
|
||||
# Forward UDP 51820 on your router to this server for external VPN access.
|
||||
|
||||
register_service wg-easy utilities "WireGuard VPN with web management UI (wg-easy)" 51821
|
||||
|
||||
install_wg-easy() {
|
||||
require_docker || return 1
|
||||
|
||||
local WGEASY_DIR="$DOCKER_DIR/wg-easy"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] wg-easy would:"
|
||||
echo " - Create $WGEASY_DIR with docker-compose.yml + .env (config/)"
|
||||
echo " - Auto-detect public IP for WG_HOST"
|
||||
echo " - Generate a random web UI password"
|
||||
echo " - Expose port 51821 (web UI) + 51820/udp (VPN)"
|
||||
echo " - Require router port-forward: UDP 51820 → this server"
|
||||
echo " - Offer a Caddy reverse proxy and to start the container"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$WGEASY_DIR"
|
||||
ensure_docker_dir_ownership "$WGEASY_DIR"
|
||||
cd "$WGEASY_DIR" || return 1
|
||||
|
||||
# Auto-detect public IP as default for WG_HOST
|
||||
local PUBLIC_IP WG_HOST WG_PASSWORD
|
||||
PUBLIC_IP=$(curl -s --connect-timeout 5 ifconfig.me 2>/dev/null || echo "your-public-ip")
|
||||
WG_PASSWORD=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
||||
|
||||
prompt_text "Public IP or hostname for VPN [$PUBLIC_IP]:" "$PUBLIC_IP" WG_HOST
|
||||
|
||||
cat > docker-compose.yml << 'WGEASY_COMPOSE'
|
||||
name: wg-easy
|
||||
|
||||
services:
|
||||
wg-easy:
|
||||
image: ghcr.io/wg-easy/wg-easy:latest
|
||||
container_name: wg-easy
|
||||
hostname: wg-easy
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
sysctls:
|
||||
- net.ipv4.ip_forward=1
|
||||
- net.ipv4.conf.all.src_valid_mark=1
|
||||
environment:
|
||||
- WG_HOST=${WG_HOST}
|
||||
- PASSWORD=${WG_PASSWORD}
|
||||
- WG_DEFAULT_DNS=1.1.1.1
|
||||
volumes:
|
||||
- ./config:/etc/wireguard
|
||||
ports:
|
||||
- "51820:51820/udp"
|
||||
- "51821:51821/tcp"
|
||||
WGEASY_COMPOSE
|
||||
|
||||
cat > .env << WGEASY_ENV
|
||||
WG_HOST=$WG_HOST
|
||||
WG_PASSWORD=$WG_PASSWORD
|
||||
WGEASY_ENV
|
||||
|
||||
mkdir -p config
|
||||
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$WGEASY_DIR"
|
||||
log_success "wg-easy configured at $WGEASY_DIR"
|
||||
|
||||
configure_caddy_for_service "wg-easy" "51821" "vpn"
|
||||
|
||||
write_readme "$WGEASY_DIR" << MD
|
||||
# wg-easy
|
||||
|
||||
WireGuard VPN with a web UI for managing clients, generating QR codes,
|
||||
and monitoring connections.
|
||||
|
||||
- Web UI: http://localhost:51821
|
||||
- VPN: UDP port 51820 (forward this on your router)
|
||||
- Password: stored in \`.env\` (\`WG_PASSWORD\`)
|
||||
- VPN host: \`$WG_HOST\` (update \`WG_HOST\` in .env if your IP changes)
|
||||
- Config: \`config/\`
|
||||
|
||||
## Manage
|
||||
\`\`\`bash
|
||||
cd $WGEASY_DIR
|
||||
docker compose up -d # start
|
||||
docker compose down # stop
|
||||
docker compose logs -f # logs
|
||||
docker compose pull && docker compose up -d # update
|
||||
\`\`\`
|
||||
|
||||
## Router setup
|
||||
Forward **UDP port 51820** to this server's LAN IP for external VPN access.
|
||||
|
||||
## Adding clients
|
||||
Open http://localhost:51821, log in with your password, click "+ New Client",
|
||||
download or scan the QR code with the WireGuard app.
|
||||
MD
|
||||
|
||||
local START_WGEASY=""
|
||||
prompt_yn "Start wg-easy now? (y/n):" "y" START_WGEASY
|
||||
if [ "$START_WGEASY" = "y" ] || [ "$START_WGEASY" = "Y" ]; then
|
||||
docker compose up -d && log_success "wg-easy started" || log_warning "Failed to start — check: docker compose logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Web UI: http://localhost:51821"
|
||||
echo " Password: $WG_PASSWORD (saved in .env)"
|
||||
echo " Router: forward UDP 51820 → this server for external VPN access"
|
||||
echo ""
|
||||
}
|
||||
Reference in New Issue
Block a user