v1.0.0
ControlPulse Documentation
ControlPulse is an open-source, self-hosted server control panel built in Go. It gives you a clean browser-based UI to manage everything on a Linux server — Nginx sites, MySQL databases, SSL certificates, firewall rules, cron jobs, files, and a live terminal — without needing SSH or cPanel.
Getting Started
Requirements
| Environment | Requirements |
|---|---|
| Linux (production) | Ubuntu 22.04+ or Debian 11+. The installer handles all dependencies. |
| Manual build | Go 1.21+ |
| macOS (development) | Go 1.21+. Runs in mock mode — no real daemons needed. |
Quick install
$ curl -sSL https://controlpulse.in/install.sh | sudo bash
After install the panel is available at http://your-server-ip:8888. Your credentials are printed at the end of the install and saved to /etc/controlp/controlp.env.
Installation
One-line install (Ubuntu / Debian)
$ curl -sSL https://controlpulse.in/install.sh | sudo bash
The script will:
- Install Nginx, MySQL, PHP 8.3-FPM, UFW, Fail2Ban, and Certbot via
apt - Download the ControlPulse binary to
/opt/controlp/ - Create a systemd service that starts on boot and restarts on failure
- Generate a secure random password and save it to
/etc/controlp/controlp.env(chmod 600) - Open port
8888in UFW - Print your panel URL and login credentials
Uninstall
$ curl -sSL https://controlpulse.in/uninstall.sh | sudo bash
Stops and removes the service, binary, and firewall rule. Optionally removes credentials.
Build from source
# Requires Go 1.21+ $ git clone https://github.com/mehranshahmiri/controlpulse.git $ cd controlpulse $ go build -o controlp main.go $ ./controlp
Run as a systemd service
The installer handles this automatically. For manual setups:
[Unit] Description=ControlPulse Server Panel After=network.target mysql.service nginx.service [Service] ExecStart=/opt/controlp/controlp WorkingDirectory=/opt/controlp EnvironmentFile=/etc/controlp/controlp.env Restart=on-failure [Install] WantedBy=multi-user.target
$ systemctl daemon-reload $ systemctl enable --now controlp
macOS development mode
$ go run main.go
On macOS, all packages detect runtime.GOOS == "darwin" and switch to local mock paths. No real Nginx, MySQL, or cron is touched. Mock files are auto-created under ./mock_nginx/ and ./www/.
Configuration
ControlPulse is configured entirely via environment variables. There are no config files.
| Variable | Default | Description |
|---|---|---|
CONTROLP_USER | admin | Login username |
CONTROLP_PASS | password123 | Login password — change this |
EnvironmentFile or export them in your shell before running.
CONTROLP_USER=admin CONTROLP_PASS=your-strong-password
The panel listens on port 8888. This is not configurable in v1.0.0.
Dashboard
The dashboard is the home screen at /. It shows real-time system stats pulled via gopsutil:
- CPU usage percentage
- RAM used / total
- Disk used / total
- Server IP address
Stats refresh on every page load. There is no auto-polling in v1.0.0.
Domains & Nginx
Found at /sites. Manages Nginx virtual host configs in /etc/nginx/sites-available/ and symlinks them into /etc/nginx/sites-enabled/.
Creating a domain
Enter a domain name and click Create Domain. ControlPulse will:
- Write an Nginx config to
/etc/nginx/sites-available/yourdomain.com - Symlink it into
sites-enabled/ - Create the webroot directory at
/var/www/html/yourdomain.com/ - Create a placeholder
index.html - Reload Nginx
Deleting a domain
Removes the config and symlink. The webroot directory is not deleted to prevent accidental data loss.
SSL Certificates
SSL is provisioned via Certbot (Let's Encrypt). Click the SSL button next to any domain in the Sites list.
ControlPulse runs:
certbot --nginx -d yourdomain.com --non-interactive --agree-tos -m admin@yourdomain.com
Database
Found at /database. Manages MySQL databases and users.
Creating a database
Enter a database name and username. ControlPulse will:
- Create the database as
db_yourname - Create a MySQL user as
user_yourname - Generate a secure random 16-character password
- Grant all privileges on the database to the user
- Display the credentials — copy them immediately, they are not stored
phpMyAdmin
The database page shows an Install phpMyAdmin button if phpMyAdmin is not detected. Once installed, an Open phpMyAdmin button appears that launches it in a new tab at /phpmyadmin.
App Blueprints
Found at /blueprints. One-click deployment of pre-configured web applications.
Available blueprints
| App | What it does |
|---|---|
| WordPress | Downloads WordPress, creates a MySQL database, writes wp-config.php, sets file permissions |
| Laravel | Clones a Laravel skeleton via Composer, sets up storage permissions, creates .env |
Select a domain (or type a new one), provide a database suffix, and click Install. If the domain doesn't exist yet, ControlPulse creates the Nginx site first.
File Manager
Found at /files. A browser-based file browser rooted at /var/www/html/ on Linux (./mock_nginx/html/ on macOS).
Capabilities
- Browse — navigate the directory tree in the left panel
- Edit — click any text file to open it in the Monaco code editor with syntax highlighting
- Save — Ctrl+S or the Save button writes the file back
- Upload — drag and drop or select a file to upload into the current directory
- Create — create a new file or folder
- Delete — delete files or directories (recursive)
getSafePath() to prevent directory traversal outside the webroot.
Environment Manager
Found at /env. A React-based standalone SPA for managing .env files across multiple projects.
Features
- Create multiple named environments (e.g. production, staging)
- Add, edit, and delete key-value pairs
- Toggle value visibility (show/hide secrets)
- Generate random secret values
- Import from raw
.envfile content - Export / download as a
.envfile - Clone an environment
Firewall
Found at /firewall. Manages UFW (Uncomplicated Firewall) rules.
Adding a rule
Enter a port number and select TCP or UDP, then click Add Rule. ControlPulse runs ufw allow PORT/proto.
Removing a rule
Click Delete next to any rule. ControlPulse runs ufw delete allow PORT/proto.
Fail2Ban
Found at /fail2ban. Shows active Fail2Ban jails and their currently banned IPs.
- Lists all enabled jails (SSH, Nginx, etc.)
- Shows currently banned IPs per jail
- Unban any IP with one click (
fail2ban-client set JAIL unbanip IP)
Cron Jobs
Found at /cron. Reads and writes the system crontab.
Adding a job
Enter a cron schedule expression and a shell command, then click Add. Standard cron syntax:
# ┌── minute (0-59) # │ ┌── hour (0-23) # │ │ ┌── day of month (1-31) # │ │ │ ┌── month (1-12) # │ │ │ │ ┌── day of week (0-7, 0 and 7 are Sunday) * * * * * /path/to/command
Deleting a job
Click Delete next to any job. The job is removed from the crontab immediately.
Terminal
Found at /terminal. A full PTY terminal running in the browser over WebSocket.
- Spawns a
bashshell via creack/pty - Bidirectional I/O over WebSocket at
/ws/terminal - Runs as the same user as the ControlPulse process (typically root when installed via the script)
- Opens in a new tab (uses the empty layout, no sidebar)
Logs & System Updates
Logs
Found at /logs. Reads the last portion of common system log files:
- Nginx access log —
/var/log/nginx/access.log - Nginx error log —
/var/log/nginx/error.log - PHP-FPM log —
/var/log/php8.3-fpm.log - MySQL error log —
/var/log/mysql/error.log - System auth log —
/var/log/auth.log
System Updates
Found at /updates. Runs apt list --upgradable to list available package updates and apt-get upgrade -y to apply them.
Service Control
Found at /settings. Restart Nginx, PHP-FPM, or MySQL with a single click. Service names are whitelisted to prevent command injection.
Architecture
ControlPulse is a single Go binary. The binary embeds all HTML templates and static assets at build time using //go:embed.
Package structure
controlpulse/ ├── main.go # Fiber app, all routes wired here ├── internal/ │ ├── auth/ # Session store, login/logout, Protected() middleware │ ├── nginx/ # sites-available/enabled management │ ├── db/ # MySQL, phpMyAdmin install │ ├── system/ # CPU/RAM/disk stats, service restart, apt updates │ ├── cron/ # Crontab parse and write │ ├── firewall/ # UFW rules │ ├── ssl/ # Certbot wrapper │ ├── security/ # Fail2Ban jail status and unban │ ├── terminal/ # WebSocket PTY bridge │ ├── fs/ # File browser, editor, upload │ ├── blueprints/ # WordPress and Laravel installers │ └── logs/ # Log file reader ├── views/ │ ├── layouts/ │ │ ├── main.html # App shell (sidebar + header) │ │ └── empty.html # Bare layout for login and API partials │ └── partials/ # HTMX fragments for list refreshes └── public/ # Logo, favicon, JS
Request flow
- All requests hit Fiber middleware in
main.go - The
auth.Protected()middleware checks the session on every route except/loginand/logout - UI pages render with
layouts/mainand return full HTML - HTMX API endpoints render with
layouts/emptyand return HTML fragments that HTMX swaps into the page /ws/terminalupgrades to WebSocket and bridges the browser to a PTY
macOS mock mode
Every package checks runtime.GOOS at startup and switches paths:
| Package | Linux path | macOS path |
|---|---|---|
nginx | /etc/nginx/sites-* | ./mock_nginx/sites-* |
fs | /var/www/html | ./mock_nginx/html |
blueprints | /var/www/html | ./www |
cron | system crontab | ./mock_cron |
system | real systemctl | mock (prints to stdout) |
Security
- Session auth — all routes except
/loginand/logoutrequire a valid session (24-hour expiry) - Command injection prevention — service names passed to
systemctlare checked against a strict whitelist ininternal/system/actions.go - Directory traversal prevention — the file manager uses
getSafePath()to ensure all paths resolve inside the webroot - Domain sanitization — domain names are sanitized before being used in Nginx config file names
- Credentials file permissions —
/etc/controlp/controlp.envis written withchmod 600 - Default password — the installer generates a random password; the hardcoded default
password123is only a fallback for local development
Contributing
Pull requests are welcome. For large changes, open an issue first to discuss the approach.
Local setup
$ git clone https://github.com/mehranshahmiri/controlpulse.git $ cd controlpulse $ go run main.go
The server starts on :8888. On macOS it runs in mock mode — no real system daemons are touched.
Code style
- Each
internal/package is isolated — no imports between packages - All system calls go through
exec.Command() - OS detection via
runtime.GOOSat package init - No test suite currently — contributions adding tests are very welcome