Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/envloom/envloom/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Envloom uses Nginx as the web server for all PHP sites with automatic configuration generation, local SSL certificate management, per-site logging, and hot-reload capabilities. Nginx is installed automatically during bootstrap.

Installation

Automatic Bootstrap

Nginx downloads and configures automatically on first launch:
1

Fetch Latest Release

Queries GitHub API for latest nginx/nginx release
2

Download ZIP

Downloads the Windows ZIP (e.g., nginx-1.25.3.zip)
3

Extract and Configure

Extracts to bin/nginx/<version>/ and flattens nested directory structure
4

Set Current Link

Creates junction at bin/nginx/current pointing to installed version
5

Configure Sites Include

Modifies conf/nginx.conf to include site configs from sites/*.conf
Nginx installation is non-blocking. The app continues loading while Nginx downloads in the background.

Version Detection

Installed Nginx version is detected by:
  1. Scanning bin/nginx/ for directories with nginx.exe
  2. Running nginx.exe -v to extract version string
  3. Parsing output like nginx version: nginx/1.25.3
fn detect_nginx_version_from_binary(nginx_root: &PathBuf) -> Option<String> {
    let nginx_exe = nginx_root.join("current").join("nginx.exe");
    let output = Command::new(&nginx_exe).arg("-v").output().ok()?;
    // Parse version from stderr output
}

Manual Installation

If auto-install fails, manually place Nginx in bin/nginx/<version>/:
# Download from nginx.org
curl -O https://nginx.org/download/nginx-1.25.3.zip

# Extract
unzip nginx-1.25.3.zip

# Move to Envloom
move nginx-1.25.3 C:\path\to\envloom\bin\nginx\1.25.3

# Create junction
mklink /J C:\path\to\envloom\bin\nginx\current C:\path\to\envloom\bin\nginx\1.25.3

Configuration

Main Configuration (nginx.conf)

Envloom modifies the default nginx.conf to include site configs:
http {
    # ... default config ...
    
    # Envloom global logs
    error_log     C:/path/to/envloom/logs/nginx/error.log;
    access_log    C:/path/to/envloom/logs/nginx/access.log;
    
    # Include all site configs
    include       C:/path/to/envloom/sites/*.conf;
}
This injection happens automatically via ensure_nginx_sites_include().

Site Configuration Generation

Each site gets its own config file in sites/<domain>.conf:
server {
    listen 80;
    server_name mysite.test;
    access_log C:/path/to/logs/nginx/sites/mysite.test.access.log;
    error_log C:/path/to/logs/nginx/sites/mysite.test.error.log;
    
    root C:/path/to/mysite/public;
    index index.php index.html index.htm;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9083;  # PHP 8.3 FPM port
    }
}

Path Normalization

Windows paths are converted to Nginx format:
fn to_nginx_path(path: &Path) -> String {
    path.to_string_lossy().replace('\\', "/")
}

// Example:
// C:\Users\Dev\envloom\sites\mysite
// → C:/Users/Dev/envloom/sites/mysite

PHP-FPM Integration

Each site config references the PHP-FPM port for its assigned PHP version:
let php_fpm_port = line_port(php_config.base_port, &site.php_version);
// base_port: 9000
// PHP 8.3 → 9083
// PHP 8.2 → 9082

write_nginx_site_config(
    &nginx_root,
    &sites_dir,
    &site.domain,
    &site.path,
    php_fpm_port,  // Injected into fastcgi_pass
    site.ssl_enabled
);

Service Management

Starting Nginx

Nginx starts automatically if autoStartServices is enabled:
Start-Process -WindowStyle Hidden `
  -FilePath "bin/nginx/current/nginx.exe" `
  -WorkingDirectory "bin/nginx/current"
Nginx must start from its own directory (-p flag or working directory) to correctly resolve relative paths in nginx.conf.

Stopping Nginx

# Graceful shutdown
nginx.exe -s quit

# Fast shutdown
nginx.exe -s stop

# Via PowerShell (Envloom method)
Get-Process -Name 'nginx' | 
  Where-Object { $_.Path -like 'bin/nginx/*' } |
  Stop-Process -Force
Envloom stops Nginx automatically on app exit.

Reloading Configuration

Reload Nginx without downtime after config changes:
# Via CLI (future)
loom reload

# Direct Nginx command
nginx.exe -s reload

# Via Envloom API
await reloadNginx();
Always test config before reload:
nginx.exe -t
Envloom automatically tests config before starting or reloading.

Process Detection

fn is_process_running(name: &str) -> bool {
    // Checks Windows task list for nginx.exe
}

if !is_process_running("nginx.exe") {
    start_nginx_if_needed(&nginx_root)?;
}

SSL Certificate Management

Local Certificate Authority

Envloom creates a persistent local CA for signing site certificates:
// CA stored at sites/ca/
sites/
├── ca/
│   ├── ca.crt      # CA certificate (trust this in browser)
│   ├── ca.key      # CA private key
│   └── ca.der      # CA certificate (DER format)
├── certs/
│   ├── mysite.test.crt
│   ├── mysite.test.key
│   └── ...
└── *.conf

Generating Site Certificates

Certificates are generated using the rcgen crate:
1

Load or Create CA

If CA doesn’t exist, generate new RSA 4096-bit CA with 10-year validity
2

Generate Site Keypair

Create RSA 2048-bit keypair for the site domain
3

Sign with CA

Sign site certificate with CA, valid for 825 days
4

Write Files

Save <domain>.crt and <domain>.key to sites/certs/
fn ensure_site_ssl_cert(
    sites_dir: &Path, 
    domain: &str
) -> Result<(PathBuf, PathBuf), String> {
    let ca = ensure_local_ca(sites_dir)?;
    let cert_dir = sites_dir.join("certs");
    
    let crt_path = cert_dir.join(format!("{domain}.crt"));
    let key_path = cert_dir.join(format!("{domain}.key"));
    
    if crt_path.exists() && key_path.exists() {
        return Ok((crt_path, key_path));
    }
    
    // Generate new certificate signed by CA
    let site_cert = generate_site_certificate(domain, &ca)?;
    write_pem(&crt_path, &site_cert.certificate)?;
    write_pem(&key_path, &site_cert.private_key)?;
    
    Ok((crt_path, key_path))
}

Trusting the CA

To eliminate browser warnings, trust the CA certificate:
# Import CA to Trusted Root Certification Authorities
certutil -addstore -user Root sites\ca\ca.crt

# Or via UI
# Double-click ca.crt > Install Certificate > Current User > 
# Place in: Trusted Root Certification Authorities

SSL Toggle Per Site

// Enable SSL for a site
await toggleSiteSSL(siteId, true);

// Regenerates nginx config with:
// - HTTP redirect to HTTPS
// - HTTPS server block with ssl_certificate directives
// - Reloads nginx

Bulk SSL Operations

From systray menu:
// Enable SSL for all sites
TRAY_MENU_SSL_ALL_ON => {
    for site in sites {
        site.ssl_enabled = true;
        regenerate_nginx_config(&site);
    }
    reload_nginx();
}

// Disable SSL for all sites
TRAY_MENU_SSL_ALL_OFF => {
    for site in sites {
        site.ssl_enabled = false;
        regenerate_nginx_config(&site);
    }
    reload_nginx();
}

Logging

Global Logs

logs/nginx/
├── access.log   # All HTTP requests across all sites
├── error.log    # Nginx startup errors and warnings
└── sites/
    ├── mysite.test.access.log
    ├── mysite.test.error.log
    └── ...

Per-Site Logs

Each site gets dedicated access and error logs:
server {
    access_log C:/path/to/logs/nginx/sites/mysite.test.access.log;
    error_log  C:/path/to/logs/nginx/sites/mysite.test.error.log;
    # ...
}

Log Viewing

Navigate to Logs page > Nginx tab:
  • General selector: Global access/error logs
  • Per-site selector: Individual site logs
  • Uses @melloware/react-logviewer for syntax highlighting

Log Reconciliation

Orphan log files are cleaned up automatically:
fn reconcile_nginx_site_configs(sites_dir: &Path, domains: &[String]) {
    // Remove *.conf for deleted sites
    // Remove *.access.log and *.error.log for deleted sites
}

Hosts File Management

Envloom manages the Windows hosts file for local domains:

Hosts Block Structure

# Envloom generated Hosts - do not edit manually
127.0.0.1 mysite.test
127.0.0.1 another.test
127.0.0.1 project.test
# End Envloom Hosts
Envloom owns entries within its block. Manual edits will be overwritten. For custom domains, add them outside the Envloom block.

Automatic Updates

Hosts file updates automatically when:
  • New site is created
  • Site domain changes
  • Site is unlinked/deleted

Elevated Permissions

Modifying hosts requires admin rights. Envloom handles this via UAC:
// Attempt direct write
let result = fs::write(&hosts_path, new_content);

if result.is_err() {
    // Elevate and retry via PowerShell
    let script = format!(
        "Start-Process -Verb RunAs powershell -ArgumentList \
         '-NoProfile -Command \"Set-Content -Path {} -Value ...\""
    );
    run_powershell(&script)?;
}

Hosts Reconciliation

Envloom reconciles the hosts file on startup:
// Move orphan Envloom domains into the managed block
// Remove domains for deleted sites
// Preserve non-Envloom entries (e.g., Herd sites)
Envloom preserves hosts entries from other tools like Laravel Herd by using separate comment blocks.

Configuration Reconciliation

Envloom maintains consistency between sites and Nginx configs:

On Startup

// Scan sites directory for *.conf files
// Compare against registered sites
// Remove configs for unregistered sites
// Regenerate configs for sites with missing *.conf

On Site Operations

  • Create site: Generate new <domain>.conf
  • Update PHP version: Regenerate config with new FPM port
  • Toggle SSL: Regenerate config with/without HTTPS
  • Unlink site: Remove <domain>.conf

Orphan Cleanup

fn reconcile_nginx_site_configs(sites_dir: &Path, domains: &[String]) {
    let managed: HashSet<String> = domains.iter()
        .map(|d| d.trim().to_lowercase())
        .collect();
    
    for entry in fs::read_dir(sites_dir)? {
        if entry.extension() == "conf" {
            let domain = entry.file_stem()?;
            if !managed.contains(&domain) {
                fs::remove_file(entry)?;  // Orphan
            }
        }
    }
}

CLI Commands

Direct Nginx Commands

# Test configuration
nginx -t

# Start (if not running)
nginx

# Stop gracefully
nginx -s quit

# Stop immediately
nginx -s stop

# Reload config
nginx -s reload

# Reopen log files
nginx -s reopen

Envloom CLI (Future)

# Reload all services
loom reload

# View nginx logs
loom logs nginx

# Open config directory
loom open nginx configs

Troubleshooting

Nginx Won’t Start

Check error log first:
type logs\nginx\error.log
Find conflicting process:
netstat -ano | findstr :80
netstat -ano | findstr :443

# Kill process (replace PID)
taskkill /PID 1234 /F
Common conflicts: IIS, Apache, Skype
Test config:
nginx -t
Fix reported errors in nginx.conf or site configs.
Run Envloom as administrator or check file permissions:
icacls bin\nginx /grant %USERNAME%:F /T

502 Bad Gateway

PHP-FPM is not running or port mismatch:
# Check PHP-FPM is running
netstat -ano | findstr :9083

# Check site config fastcgi_pass matches PHP version port
type sites\mysite.test.conf | findstr fastcgi_pass

# Restart PHP-FPM
loom reload

SSL Certificate Errors

Browser shows “Not Secure” warning:
1

Check Certificate Files Exist

dir sites\certs\mysite.test.*
2

Regenerate Certificate

await regenerateSiteSSL(siteId);
3

Trust CA Certificate

certutil -addstore -user Root sites\ca\ca.crt
4

Clear Browser Cache

Restart browser to reload certificate trust store

Site Not Resolving

Check hosts file:
type C:\Windows\System32\drivers\etc\hosts
Ensure domain is in Envloom block:
127.0.0.1 mysite.test
If missing, reconcile from Envloom dashboard.

Best Practices

Configuration Management

  • Don’t edit generated site configs manually - use Envloom UI
  • Custom nginx directives: Add to main nginx.conf outside site includes
  • Keep nginx.conf backed up before major changes

Performance Tuning

# Add to nginx.conf http block
worker_processes auto;
worker_connections 1024;
keepalive_timeout 65;
gzip on;
gzip_types text/plain text/css application/json application/javascript;

Security

# Hide nginx version
server_tokens off;

# Add security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";

Logging

# Custom log format with timing
log_format timing '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" '
                  'rt=$request_time uct=$upstream_connect_time '
                  'uht=$upstream_header_time urt=$upstream_response_time';

access_log logs/nginx/access.log timing;