This guide uses 101.99.94.121 and hestiacp30usd.vpsme.win as examples. Replace them with your actual VPS IP and hostname in every step where they appear.

Purpose

This guide is for a fresh Debian 12 VPS installation of HestiaCP. It is designed to be reusable for every new server you provision.

Important Rules

Follow these strictly. Every rule exists because breaking it has caused real production issues.

Server Details Template

Fill in your values before starting. Keep this reference handy.

ParameterValue
VPS IPYOUR_SERVER_IP
Hostname (FQDN)your.hostname.tld
SSH Port20203
Hestia Admin Usernameadmin
Hestia Admin Password••••••••
Admin Email[email protected]

Pre-Install Checks

Connect to the server and verify it is completely clean.

bash
ssh -p 20203 root@YOUR_SERVER_IP

Check OS version and detect any pre-existing packages:

bash
cat /etc/os-release | grep PRETTY
dpkg -l | grep -E "nginx|apache|mysql|mariadb|hestia"
If nginx, apache, mysql, mariadb, or hestia are already installed, do a full VPS rebuild from your provider's panel before continuing.

Hostname Setup

Set the hostname correctly. This is critical for SSL, panel cookies, and session stability.

bash
hostnamectl set-hostname hestiacp30usd.vpsme.win

Edit /etc/hosts:

bash
nano /etc/hosts

Delete everything in the file — including the # Generated by SolusVM comment and any auto-generated lines from your VPS provider. Replace with this:

/etc/hosts
127.0.0.1       localhost localhost.localdomain
127.0.1.1       hestiacp30usd.vpsme.win hestiacp30usd
101.99.94.121   hestiacp30usd.vpsme.win hestiacp30usd
::1             localhost localhost.localdomain
Replace 101.99.94.121 with your actual VPS IP and hestiacp30usd.vpsme.win with your actual hostname before saving.

Save: press Ctrl+O, then press Enter to confirm the filename, then press Ctrl+X to exit nano.

Verify:

bash
hostname -f
expected output
hestiacp30usd.vpsme.win
Correct hostname setup prevents SSL mismatch problems, bad panel cookies, and session issues.

System Update

bash
apt update && apt upgrade -y
apt install -y ca-certificates curl wget
Do not use apt dist-upgrade. It can replace kernel packages and break VPS environments.

Cloudflare DNS Before Install

Before installing HestiaCP, create the DNS record in Cloudflare Dashboard.

TypeNameContentProxy Status
A hestiacp30usd 101.99.94.121 DNS only
Replace 101.99.94.121 with your actual VPS IP and hestiacp30usd with your actual hostname subdomain.
The hostname record must stay DNS only (gray cloud). Cloudflare does not proxy port 8083. Proxying it will break panel access.

HestiaCP Installation

Download the official installer:

bash
cd /root
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh

Run the interactive installer. Answer prompts carefully:

bash
bash hst-install.sh

When asked for FQDN hostname, enter: hestiacp30usd.vpsme.win

Recommended stack choices:

NGINXyes
Apache (backend)yes
PHP-FPMyes
Multi PHPno
FTP Serveroptional
MySQL / MariaDByes
PostgreSQLno
Firewall (iptables)yes
Fail2banyes
ClamAVno
SpamAssassinno
Exim (mail)yes
This stack is chosen for stability, compatibility, and fewer surprises. ClamAV and SpamAssassin consume 500MB+ RAM each — skip them unless you really need mail scanning.

Reboot After Install

bash
reboot

Wait 30 seconds, then reconnect:

bash
ssh -p 20203 [email protected]

Verify Services

bash
v-list-sys-services

If something is stopped, inspect it:

bash
systemctl status SERVICE_NAME
journalctl -u SERVICE_NAME --no-pager -n 50

Panel Access

Preferred panel access URL:

url
https://hestiacp30usd.vpsme.win:8083
Keep the hostname DNS-only in Cloudflare. Do not proxy port 8083. Pick one access method (IP or hostname) and stay consistent throughout the session. Switching between them causes cookie and session conflicts.

Firewall Golden Rule

One firewall system only. Hestia manages iptables directly.

SSH & File Manager — Critical Fix

This is the most important edit in this guide. It prevents File Manager crashes caused by SSH port changes.

Open the SSH config file:

bash
nano /etc/ssh/sshd_config

You will see your current config already has Port 20203 (your custom SSH port). It looks like this:

what you see now
Port 20203

Add Port 22 on a new line directly above Port 20203, so it becomes:

what it should look like after your edit
Port 22
Port 20203
Do not remove or change the Port 20203 line. Do not touch anything else in the file. Just add Port 22 above it.

Save: press Ctrl+O, then press Enter to confirm the filename, then press Ctrl+X to exit nano.

Restart SSH:

bash
systemctl restart sshd
Why both ports? Hestia File Manager internally connects to localhost:22 via SFTP to browse your files. If port 22 is missing, File Manager breaks — even though your external SSH on 20203 still works fine. The firewall blocks port 22 from outside, so it only works locally. Your SSH login still uses port 20203 as before.

Hestia Firewall Rule for Custom SSH

bash
v-add-firewall-rule ACCEPT 0.0.0.0/0 20203 TCP "Custom SSH"
v-update-firewall

Verify the rule exists:

bash
v-list-firewall
Port 22 is used locally by the system (File Manager). Port 20203 is your public SSH port exposed through the firewall.

Fail2ban

bash
fail2ban-client status

Check SSH jail specifically:

bash
fail2ban-client status ssh-iptables

Hestia already created /etc/fail2ban/jail.local with all the jails, but the SSH jail is missing your custom port. Instead of editing inside nano (easy to mess up), run this single command to replace the file with the correct version. Copy and paste it directly in your terminal — not inside nano:

bash — paste directly in terminal
printf '[ssh-iptables]\nenabled  = true\nfilter   = sshd\naction   = hestia[name=SSH]\nlogpath  = /var/log/auth.log\nport     = 20203\nmaxretry = 5\nbantime  = 3600\nfindtime = 600\n\n[vsftpd-iptables]\nenabled  = true\nfilter   = vsftpd\naction   = hestia[name=FTP]\nlogpath  = /var/log/vsftpd.log\nmaxretry = 5\n\n[exim-iptables]\nenabled  = true\nfilter   = exim\naction   = hestia[name=MAIL]\nlogpath  = /var/log/exim4/mainlog\n\n[dovecot-iptables]\nenabled  = true\nfilter   = dovecot\naction   = hestia[name=MAIL]\nlogpath  = /var/log/dovecot.log\n\n[mysqld-iptables]\nenabled  = false\nfilter   = mysqld-auth\naction   = hestia[name=DB]\nlogpath  = /var/log/mysql/error.log\nmaxretry = 5\n\n[hestia-iptables]\nenabled  = true\nfilter   = hestia\naction   = hestia[name=HESTIA]\nlogpath  = /var/log/hestia/auth.log\nmaxretry = 5\n\n[roundcube-auth]\nenabled  = false\nfilter   = roundcube-auth\naction   = hestia[name=WEB]\nlogpath  = /var/log/roundcube/errors.log\nmaxretry = 5\n\n[phpmyadmin-auth]\nenabled  = true\nfilter   = phpmyadmin-syslog\naction   = hestia[name=WEB]\nlogpath  = /var/log/auth.log\nmaxretry = 5\n\n[recidive]\nenabled  = true\nfilter   = recidive\naction   = hestia[name=RECIDIVE]\nlogpath  = /var/log/fail2ban.log\nmaxretry = 5\nfindtime = 86400\nbantime  = 864000\n' > /etc/fail2ban/jail.local
This is the complete jail.local file with all Hestia default jails. The only change from the original is three lines added to [ssh-iptables]: port = 20203, bantime = 3600, and findtime = 600. The printf method avoids the double-spacing issue that cat heredocs can cause when pasting from a browser.

Now restart and verify:

bash
systemctl restart fail2ban
fail2ban-client status

File Manager Fix & Policy

Known bug in HestiaCP 1.9.x: The File Manager crashes with a 500 error because HestiaCP's custom SessionStorage.php is missing the migrate() method that FileGator expects. Reinstalling the File Manager does not fix this — you must patch the file manually.

Run this command to replace the broken file with the fixed version. Paste directly in terminal:

bash — paste directly in terminal
printf '<?php\nnamespace Filegator\\Services\\Session\\Adapters;\n\nuse Filegator\\Kernel\\Request;\nuse Filegator\\Services\\Service;\nuse Filegator\\Services\\Session\\Session;\nuse Filegator\\Services\\Session\\SessionStorageInterface;\n\nclass SessionStorage implements Service, SessionStorageInterface {\n\n        protected $request;\n        protected $config;\n\n        public function __construct(Request $request) {\n                $this->request = $request;\n        }\n\n        public function init(array $config = []) {\n                if (!$this->getSession()) {\n                        $handler = $config["handler"];\n                        $session = new Session($handler());\n                        $this->setSession($session);\n                }\n        }\n\n        public function save() {\n                $this->getSession()->save();\n        }\n\n        public function set(string $key, $data) {\n                return $this->getSession()->set($key, $data);\n        }\n\n        public function get(string $key, $default = null) {\n                return $this->getSession() ? $this->getSession()->get($key, $default) : $default;\n        }\n\n        public function invalidate() {\n                if (!$this->getSession()->isStarted()) {\n                        $this->getSession()->start();\n                }\n                $this->getSession()->invalidate();\n        }\n\n        public function migrate($destroy = false, $lifetime = null): bool {\n                if ($this->getSession() && $this->getSession()->isStarted()) {\n                        return $this->getSession()->migrate($destroy, $lifetime);\n                }\n                return false;\n        }\n\n        private function setSession(Session $session) {\n                return $this->request->setSession($session);\n        }\n\n        private function getSession(): ?Session {\n                return $this->request->getSession();\n        }\n}\n' > /usr/local/hestia/web/fm/backend/Services/Session/Adapters/SessionStorage.php

Restart Hestia:

bash
systemctl restart hestia

Test the File Manager in your browser — it should work now.

After future Hestia updates: if the File Manager breaks again with the same error, run the same command above. Hestia updates may overwrite the fix. You may also need to re-run it after v-add-sys-filemanager.

General File Manager policy:

Do not rely heavily on Hestia File Manager for production work. Use it only as a convenience tool for quick checks.

Preferred workflow tools:

NGINX Performance Tuning

No changes needed. HestiaCP's default NGINX config is already well-tuned out of the box. It includes gzip compression, epoll, file caching, Cloudflare IP support, and SSL optimization.

You can verify by checking the config:

bash
cat /etc/nginx/nginx.conf

What Hestia already sets for you:

Do not manually edit /etc/nginx/nginx.conf — Hestia may overwrite your changes on updates. If you need custom NGINX settings for a specific site, use Hestia's template system instead.

PHP-FPM Tuning

Do not edit pool configs directly. Hestia auto-generates a separate PHP-FPM pool for each domain (e.g. /etc/php/8.3/fpm/pool.d/yourdomain.conf). These files say "DO NOT MODIFY" at the top — Hestia overwrites them when rebuilding domains. To change pool settings, use Hestia's PHP-FPM templates instead.

What you can safely edit is php.ini — this applies globally to all PHP-FPM pools.

First check your PHP version:

bash
php -v

Then check the current php.ini values (replace 8.3 with your version if different):

bash
cat /etc/php/8.3/fpm/php.ini | grep -E "upload_max_filesize|post_max_size|memory_limit|max_execution_time|max_input_vars"

Hestia defaults are already reasonable. If you need more memory or execution time for heavy apps like WordPress + WooCommerce, run these commands (replace 8.3 with your PHP version):

bash
sed -i 's/^memory_limit = 128M/memory_limit = 256M/' /etc/php/8.3/fpm/php.ini
sed -i 's/^max_execution_time = 60/max_execution_time = 300/' /etc/php/8.3/fpm/php.ini
systemctl restart php8.3-fpm

Verify the changes:

bash
cat /etc/php/8.3/fpm/php.ini | grep -E "memory_limit|max_execution_time"

What these changes do:

The rest of the defaults are already good:

Save: press Ctrl+O, then press Enter to confirm the filename, then press Ctrl+X to exit nano. Then restart PHP-FPM:

bash
systemctl restart php8.3-fpm
Replace 8.3 with your actual PHP version in all commands. Check with php -v.

MariaDB Basic Optimization

Hestia's default MariaDB config is mostly comments with few active settings. Adding some InnoDB and connection tuning improves performance for WordPress and other database-heavy apps.

First check your RAM to decide buffer sizes:

bash
free -m

Then run this command to add optimization settings. These are safe for a 4GB RAM VPS — adjust innodb_buffer_pool_size if you have more or less RAM:

bash — paste directly in terminal
printf '\n# === Custom optimization (added by setup guide) ===\n[mysqld]\ninnodb_buffer_pool_size = 256M\ninnodb_log_file_size = 64M\ninnodb_flush_log_at_trx_commit = 2\ninnodb_flush_method = O_DIRECT\nmax_connections = 100\nwait_timeout = 300\ninteractive_timeout = 300\ntmp_table_size = 32M\nmax_heap_table_size = 32M\nslow_query_log = 1\nslow_query_log_file = /var/log/mysql/slow.log\nlong_query_time = 2\n' >> /etc/mysql/mariadb.conf.d/50-server.cnf
This appends to the file (uses >> not >) — it does not overwrite existing settings.

Restart MariaDB:

bash
systemctl restart mariadb

Verify MariaDB is running:

bash
systemctl status mariadb | head -5

What these settings do:

If MariaDB fails to start after this, check the error with journalctl -u mariadb --no-pager -n 20. To undo, edit the file with nano /etc/mysql/mariadb.conf.d/50-server.cnf and remove the lines you just added at the bottom.

Cloudflare for Websites

Rules for running websites behind Cloudflare with HestiaCP:

RecordProxyNotes
Panel hostnameDNS onlyAlways gray cloud
Website domainsProxiedOrange cloud for CDN + protection
Mail records (MX)DNS onlyCannot be proxied

Recommended Daily Workflow

TaskTool
Code editing & deploymentVS Code Remote SSH
File transfersSFTP (FileZilla / WinSCP)
Domain & user managementHestiaCP Panel (browser)
Database managementphpMyAdmin (via Hestia)
Emergency file browsingHestia File Manager (backup only)

Backups

Use Hestia built-in backups and keep off-server copies for critical data.

bash
v-list-user-backups admin

Quick Commands

Click any command to copy it.

$ v-list-sys-services
$ systemctl restart hestia
$ systemctl restart nginx
$ systemctl restart apache2
$ systemctl restart php8.3-fpm
$ v-list-firewall
$ v-update-firewall
$ fail2ban-client status
$ tail -f /var/log/hestia/nginx-error.log
$ df -h
$ free -m
$ apt update && apt upgrade -y

Final Checklist

Verify every item before going live.