- shares.js, users.js: JSON.stringify() inside onclick="..." attributes produced unescaped " chars that broke HTML attribute parsing — all dynamic onclick JSON now passed through esc() so the browser decodes " back to " before executing the handler - Replace browser confirm() dialogs with a styled Bootstrap modal (#deleteModal) for share and user deletion — reusable across all tabs - users.js: rename shadow variable 'confirm' → 'pwConfirm' to avoid shadowing the now-removed confirm() call - main.js: auto-refresh now reloads shares and users every 30 s, not just the dashboard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MicroNAS
Lightweight web GUI to manage SMB shares, NFS exports, and quotas on a single home server (Proxmox LXC/VM). Runs directly on the host as a systemd service.
Proxmox LXC setup
Use a privileged Debian/Ubuntu container — required for the NFS server kernel module and quotas.
On the Proxmox host, load the NFS kernel module before starting the container:
modprobe nfsd
echo nfsd >> /etc/modules
Install
Run everything below inside the LXC container as root.
1. System dependencies
apt update
apt install -y git samba nfs-kernel-server quota python3 python3-venv
2. Clone the repo
git clone https://gitea.honeyy.duckdns.org/Honey/MicroNAS.git /opt/micronas
3. Python environment
python3 -m venv /opt/micronas/venv
/opt/micronas/venv/bin/pip install flask gunicorn
4. Credentials
cat > /etc/micronas.env <<EOF
MICRONAS_USER=admin
MICRONAS_PASS=changeme
EOF
chmod 600 /etc/micronas.env
5. systemd service
cp /opt/micronas/micronas.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now micronas
Open http://<lxc-ip>:5000 in your browser.
Updates
cd /opt/micronas
git pull
systemctl restart micronas
Network
The service binds to 127.0.0.1:5000 by default. To reach it directly from the LAN, change -b 127.0.0.1:5000 to -b 0.0.0.0:5000 in micronas.service, then run systemctl daemon-reload && systemctl restart micronas.
For HTTPS, put nginx in front as a reverse proxy.
LAN or Tailscale only — never expose to the internet.
Dev / test
# Quick local run (no gunicorn needed):
MICRONAS_USER=admin MICRONAS_PASS=secret flask --app app.py run --debug --host=0.0.0.0
# Or with docker-compose (UI only — systemctl/samba calls won't work):
docker compose up
Auth
All routes require HTTP Basic Auth. Credentials come from MICRONAS_USER / MICRONAS_PASS environment variables (set via /etc/micronas.env in production). If MICRONAS_PASS is not set the app starts unprotected and logs a warning — don't do this in production.