ControlPulse
Documentation
Home GitHub v1.0.0
ControlPulse 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.

Nginx SSL MySQL Blueprints Files Firewall Fail2Ban Cron Terminal
Vibe-coded project. ControlPulse may contain bugs. Use in production at your own discretion. Bug reports and pull requests are welcome on GitHub.

Getting Started

Requirements

EnvironmentRequirements
Linux (production)Ubuntu 22.04+ or Debian 11+. The installer handles all dependencies.
Manual buildGo 1.21+
macOS (development)Go 1.21+. Runs in mock mode — no real daemons needed.

Quick install

bash
$ 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)

bash
$ 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 8888 in UFW
  • Print your panel URL and login credentials

Uninstall

bash
$ curl -sSL https://controlpulse.in/uninstall.sh | sudo bash

Stops and removes the service, binary, and firewall rule. Optionally removes credentials.

Build from source

bash
# 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:

/etc/systemd/system/controlp.service
[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
bash
$ systemctl daemon-reload
$ systemctl enable --now controlp

macOS development mode

bash
$ 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.

VariableDefaultDescription
CONTROLP_USERadminLogin username
CONTROLP_PASSpassword123Login password — change this
Never run in production with the default password. Set these in your systemd EnvironmentFile or export them in your shell before running.
/etc/controlp/controlp.env
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.

Domain names are sanitized before being written to config files to prevent path injection.

SSL Certificates

SSL is provisioned via Certbot (Let's Encrypt). Click the SSL button next to any domain in the Sites list.

ControlPulse runs:

equivalent command
certbot --nginx -d yourdomain.com --non-interactive --agree-tos -m admin@yourdomain.com
Your domain must have a valid DNS A record pointing to the server's IP before requesting a certificate. Let's Encrypt will fail if the domain can't be reached.

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

AppWhat it does
WordPressDownloads WordPress, creates a MySQL database, writes wp-config.php, sets file permissions
LaravelClones 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.

Blueprints require the MySQL root password to create databases. This password is used only for that operation and is not stored.

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)
All paths are validated with 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 .env file content
  • Export / download as a .env file
  • Clone an environment
The Environment Manager is a client-side only tool in v1.0.0. Data is not persisted to disk — it lives in browser memory only. Copy or download your env files before closing the tab.

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.

UFW must be installed and active on the server. The installer enables it automatically.

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)
Fail2Ban must be installed and running. The installer sets it up automatically.

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:

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 bash shell 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)
The terminal gives full shell access. Make sure the panel is not publicly accessible without authentication. Port 8888 should be firewall-restricted if possible.

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

project layout
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 /login and /logout
  • UI pages render with layouts/main and return full HTML
  • HTMX API endpoints render with layouts/empty and return HTML fragments that HTMX swaps into the page
  • /ws/terminal upgrades to WebSocket and bridges the browser to a PTY

macOS mock mode

Every package checks runtime.GOOS at startup and switches paths:

PackageLinux pathmacOS path
nginx/etc/nginx/sites-*./mock_nginx/sites-*
fs/var/www/html./mock_nginx/html
blueprints/var/www/html./www
cronsystem crontab./mock_cron
systemreal systemctlmock (prints to stdout)

Security

  • Session auth — all routes except /login and /logout require a valid session (24-hour expiry)
  • Command injection prevention — service names passed to systemctl are checked against a strict whitelist in internal/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.env is written with chmod 600
  • Default password — the installer generates a random password; the hardcoded default password123 is only a fallback for local development
ControlPulse does not yet support HTTPS on port 8888. For production use, consider putting it behind an Nginx reverse proxy with a certificate, and restricting port 8888 to trusted IPs only via UFW.

Contributing

Pull requests are welcome. For large changes, open an issue first to discuss the approach.

Local setup

bash
$ 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.GOOS at package init
  • No test suite currently — contributions adding tests are very welcome

Links

Made by Mehran Shahmiri, Powered by OpenLoop — GPL-3.0