SiteMgr is a comprehensive web hosting management tool for Alpine Linux that automates the creation, configuration, and management of PHP and Bun.js websites. It provides isolated, secure environments for each site using standard Linux users and permissions (without chroot jails) with automatic SSL certificate management, database provisioning, and email configuration.
- Main Script:
/usr/local/bin/sitemgr- Bash script that orchestrates all operations - Configuration:
/etc/sitemgr/- Templates and default settings - Sites Directory:
/sites/- Root directory for all hosted sites - Port Allocations:
/var/sitemgr/port_allocations- Tracks ports for Bun applications
Each site is created under /sites/DOMAIN/ with the following structure:
/sites/domain.com/ # User's home directory
├── htdocs/ # Web root for PHP sites
├── app/ # Application directory for Bun sites
├── config/ # Site configuration files
│ ├── nginx.conf # Nginx configuration
│ ├── .env # Environment variables (DB credentials)
│ ├── msmtprc # Email configuration
│ ├── dkim.conf # DKIM configuration
│ ├── dkim.key # DKIM private key
│ ├── dkim.pub # DKIM public key
│ └── readwritedirectories.txt # Directories that remain writable
├── logs/ # Access and error logs
├── tmp/ # Temporary files
├── .ssh/ # SSH keys
├── .npmrc # NPM configuration
├── .config/ # User configurations (Fish shell, etc.)
├── .npm/ # NPM cache
└── .npm-global/ # Global NPM packages
- Supports PHP versions 82, 83, 84 (dynamically detected)
- PHP-FPM pool per site with isolated configuration
- WordPress optimization available
- Automatic PHP extension installation
- Node.js/Bun.js application hosting
- Automatic port allocation (starting from 3001)
- OpenRC service management
- Proxy configuration through Nginx
- Separate System User: Each site gets a unique system user (domain without dots, max 32 chars)
- Home Directory: User home is set to
/sites/DOMAIN/for isolation - Permission Separation: Web files owned by user:nginx group
- PHP open_basedir: Restricts PHP access to site directories only
- SSH Access: Key-based authentication only (no passwords)
- Fish Shell: Default shell for site users
- SSH Access: Standard SSH access with Fish shell
- Read-only Mode: Sites can be toggled between read-only and writable states
- Directory Protection: Sensitive directories blocked in Nginx
- none: HTTP-only configuration
- self-signed: Generates self-signed certificate
- acme: Production Let's Encrypt certificates
- acme-test: Staging Let's Encrypt certificates
- Automatic certificate generation and renewal
- Nginx configuration updates based on HTTPS mode
- ACME challenges via webroot method
Each site automatically gets:
- Dedicated MariaDB database (named after username)
- Database user with full privileges on their database
- Credentials stored in
/sites/DOMAIN/config/.env - Password is stored in plain text in the .env file
- MSMTP configuration for outbound email
- DKIM key generation for email authentication
- DKIM key generation and DNS record creation in config/dkim.conf
Located in /etc/sitemgr/templates/:
nginx-php.conf- HTTPS-enabled PHP sitenginx-php-http.conf- HTTP-only PHP sitenginx-wordpress.conf- HTTPS WordPress optimizednginx-wordpress-http.conf- HTTP WordPress
nginx-bun.conf- HTTPS-enabled Bun proxynginx-bun-http.conf- HTTP-only Bun proxynginx-bun-https.conf- HTTPS Bun proxy with SSL
{{DOMAIN}}- Site domain name{{PHP_VERSION}}- PHP version number (81, 82, 83){{PORT}}- Bun application port{{USERNAME}}- System username
php-fpm-pool.conf configures:
- Process manager settings
- Memory limits
- Execution timeouts
- Socket permissions
- Environment variables
DEFAULT_PHP_VERSION=83
PHP_FPM_MAX_CHILDREN=5
PHP_FPM_START_SERVERS=2
PHP_FPM_MIN_SPARE=1
PHP_FPM_MAX_SPARE=3
PHP_FPM_MAX_REQUESTS=500
PHP_MEMORY_LIMIT=256M
PHP_MAX_EXECUTION_TIME=300
PHP_POST_MAX_SIZE=64M
PHP_UPLOAD_MAX_FILESIZE=64M# PHP site with HTTPS
sitemgr --create example.com --https acme --php 82
# WordPress site
sitemgr --create blog.com --https acme --php 82 --wordpress
# Bun.js site
sitemgr --create app.com --https self-signed --type bun
# HTTP-only site
sitemgr --create test.local --https none --php 83# List all sites
sitemgr --list
# Remove a site (with confirmation)
sitemgr --remove example.com
# Change PHP version
sitemgr --change-php example.com 83
# Change HTTPS mode
sitemgr --change-https example.com acme
# Generate DKIM keys
sitemgr --dkim-generate example.comSiteMgr includes a powerful permission management system that allows users to easily switch between read-only (secure) and writable modes for their sites.
The site command is available to all site users and can be run from anywhere within their site directory:
# Check current permission status
site --status
site -s
# Enable write mode for updates
site --writable
site -w
# Return to read-only mode (secure)
site --readonly
site -r
# Show help
site --help
site -h-
Read-only Mode (Production/Secure):
- Directories:
2550(r-xr-s---) - Files:
440(r--r-----) - Prevents unauthorized modifications
- Recommended for production sites
- Directories:
-
Writable Mode (For Updates):
- Directories:
2750(rwxr-s---) - Files:
640(rw-r-----) - Allows CMS updates, plugin installations
- Should be temporary - return to read-only when done
- Directories:
-
Security Features:
- Removes all "others" permissions (no access for other users)
- Maintains proper nginx group ownership
- Setgid on directories ensures new files inherit correct group
- Integrates with doas for privilege escalation
-
Smart Detection:
- Automatically finds web root from nginx configuration
- Handles special cases for WordPress and Drupal
- Respects exceptions in
config/readwritedirectories.txt
# SSH into your site
ssh user@example.com
# Check current status
site -s
# Enable write mode for WordPress updates
site -w
# Perform updates via WordPress admin panel
# ...
# Return to read-only mode
site -rWhen users log in, they see:
- Current permission status of their site
- Available commands for permission management
- Warnings if the site is currently writable
The script correctly selects Nginx templates based on the HTTPS mode:
if [ "$https_mode" = "none" ]; then
# Use HTTP-only template for no HTTPS
template="/etc/sitemgr/templates/nginx-php-http.conf"
elif [ "$https_mode" = "acme" ] || [ "$https_mode" = "acme-test" ]; then
# Start with HTTP for ACME validation
template="/etc/sitemgr/templates/nginx-php-http.conf"
else
# Use HTTPS template for self-signed
template="/etc/sitemgr/templates/nginx-php.conf"
fiThis ensures:
--https noneuses HTTP-only templates--https acmeand--https acme-teststart with HTTP for certificate validation--https self-signeduses HTTPS templates with the self-signed certificate
- Bun applications get sequential ports starting from 3001
- Port allocations stored in
/var/sitemgr/port_allocations - Format:
domain port
- Username derived from domain (dots removed, max 32 chars)
- Example:
example.combecomes usernameexamplecom - Home directory:
/sites/example.com/(note: directory uses full domain, not username)
- Example:
- User added to nginx group for file permissions
- Password authentication disabled (key-only)
- Automatic SSH key deployment from root's key (~/.ssh/id_ed25519.pub)
The script creates an isolated environment by:
- Creating a dedicated system user for each site
- Setting user home directory to
/sites/DOMAIN/ - Configuring PHP-FPM pools with open_basedir restrictions
- Setting proper file permissions and ownership
- Configuring SSH access with security restrictions
- PHP-FPM pool created per site
- Pool configuration based on defaults
- Automatic PHP-FPM reload on changes
- Each user gets their own Bun installation at
/sites/DOMAIN/.bun/ - PM2 process manager for user-controlled app management
- PM2 ecosystem config at
/sites/DOMAIN/ecosystem.config.js - Control script at
/sites/DOMAIN/control.shfor easy management - Users can manage their apps without root privileges:
./control.sh start- Start the application./control.sh stop- Stop the application./control.sh restart- Restart the application./control.sh status- View application status./control.sh logs- View application logs
/sites/DOMAIN/- username:nginx (user's home directory, mode 750)/sites/DOMAIN/htdocs/- username:nginx (mode 750)/sites/DOMAIN/app/- username:nginx (mode 750)/sites/DOMAIN/config/- username:nginx (mode 750)/sites/DOMAIN/logs/- username:nginx (mode 750)/sites/DOMAIN/logs/nginx/- nginx:username (mode 750) - tamper-proof nginx logs/sites/DOMAIN/logs/php/- username:username (mode 750)/sites/DOMAIN/.ssh/- username:username (mode 700)/sites/DOMAIN/tmp/- username:username (mode 700)
- Configuration files: 600 (sensitive) or 640/644 (nginx needs access)
- Web files: 644 (or 444 in read-only mode)
- SSH keys: 600
- Scripts (site-readonly/writable): 755
Sites include helper scripts in the user's home directory:
Located at /sites/DOMAIN/site-readonly
Makes web root read-only except directories listed in readwritedirectories.txt
The readwritedirectories.txt file contains full paths, for example:
/sites/domain.com/tmp
/sites/domain.com/logs
/sites/domain.com/htdocs/wp-content/uploads
Located at /sites/DOMAIN/site-writable
Restores write permissions to web root
- Generated using
openssl rand -base64 16 - Stored in plain text in
.envfile - Used directly from environment variables
When --wordpress flag is used:
- Specific Nginx rules for WordPress
- Additional writable directories configured
- Performance optimizations for WordPress assets
The following commands are defined but not functional:
--info- Show detailed site information--backup- Create site backup--start- Start site services--stop- Stop site services--restart- Restart site services--status- Show service status
- Root Password: MariaDB root password must be in
/root/.mysql_root - User Isolation: Users are isolated through Unix permissions and PHP restrictions
- SSL Certificates: Stored in
/etc/nginx/ssl/with appropriate permissions - Database Passwords: Stored in plain text - use proper secrets management in production
- PHP Security: open_basedir restricts PHP scripts to site directories only
- nginx: cannot load certificate: Check if HTTPS mode matches certificate availability
- PHP-FPM socket not found: Ensure PHP-FPM service is running
- Permission denied: Check file ownership and group membership
- SSH access fails: Verify SSH key is in
/sites/DOMAIN/.ssh/authorized_keys
- Nginx access/error:
/sites/DOMAIN/logs/ - PHP-FPM:
/var/log/php*.log - MSMTP:
/sites/DOMAIN/logs/msmtp.log - System:
/var/log/messages
The script is designed to be extended with:
- Additional PHP versions (modify
get_available_php_versions()) - New site types (add to site_type logic)
- Custom templates (add to
/etc/sitemgr/templates/) - Additional HTTPS providers (extend HTTPS mode handling)