Prerequisites
- OS: Rocky Linux / RHEL 8 or 9
- Node.js: 20.4 or newer — this guide installs the Node.js 22 module, since Node.js 20 reached end-of-life in April 2026
- Access: Root or
sudoprivileges - Network: Static IP address or domain name (e.g.,
192.168.1.100ormonitor.yourdomain.com)
Note
Upstream ships Uptime Kuma as
louislam/uptime-kuma:2, and the 2.x line requires Node.js ≥ 20.4. Everything below is the supported non-Docker path, which runs the same code as the container image. If you are upgrading an existing 1.x install rather than starting fresh, back updata/first — 2.x migrates the database on first boot and the upgrade is not reversible.
1. Install & Run Uptime Kuma
We will clone Uptime Kuma and run it as a service using Node.js and PM2 (or natively via systemd).
Install Node.js & Clone Repository
# Update system packages
sudo dnf update -y
# Enable and install the current Node.js LTS module & Git
sudo dnf module enable nodejs:22 -y
sudo dnf install nodejs git -y
# Confirm the runtime Uptime Kuma requires (>= 20.4)
node -v
# Clone Uptime Kuma to /opt
cd /opt
sudo git clone https://github.com/louislam/uptime-kuma.git
cd uptime-kuma
# Install dependencies and set up Uptime Kuma
sudo npm run setupNote
Uptime Kuma keeps its data in
data/as an SQLite database, which needs POSIX file locks. Keep that directory on a local filesystem — an NFS mount or a networked share is a common cause of silent database corruption. On 2.3.2 and later the single-connection default (UPTIME_KUMA_SQLITE_SINGLE_CONNECTION) exists for exactly this reason.
Option A: Run using PM2 (Recommended)
# Install PM2 globally, with log rotation so logs can't fill the disk
sudo npm install pm2 -g
sudo pm2 install pm2-logrotate
# Start Uptime Kuma service
sudo pm2 start server/server.js --name uptime-kuma
# Persist the process list and enable PM2 on boot
sudo pm2 save
sudo pm2 startup
# (Run the command printed by the line above, then reload with `pm2 save`)Option B: Run using Systemd Service
Create the service file at /etc/systemd/system/uptime-kuma.service:
[Unit]
Description=Uptime Kuma - Self-hosted monitoring tool
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/uptime-kuma
ExecStart=/usr/bin/node /opt/uptime-kuma/server/server.js
Restart=on-failure
Environment=NODE_ENV=production PORT=3001 HOST=127.0.0.1
[Install]
WantedBy=multi-user.targetEnable and start the systemd service:
sudo systemctl daemon-reload
sudo systemctl enable uptime-kuma
sudo systemctl start uptime-kumaTip
HOST=127.0.0.1keeps Uptime Kuma reachable only through Apache, so port3001is never exposed directly. The equivalent for PM2 and for local runs isUPTIME_KUMA_HOST=127.0.0.1in a.envfile in the project root, or the--host=127.0.0.1server argument.
2. Configure System Security
SELinux and Firewalld block internal reverse proxy traffic and external web access by default.
Configure SELinux
Allow Apache (httpd) to connect to backend network services (Uptime Kuma running on port 3001):
sudo setsebool -P httpd_can_network_connect 1Configure Firewall
Open HTTP (80) and HTTPS (443) ports:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload3. Install & Configure Apache Reverse Proxy
Install Apache and configure it to proxy traffic to Uptime Kuma, including WebSocket upgrades and ModSecurity rule bypasses.
Install Apache
sudo dnf install httpd -y
sudo systemctl enable httpd --now
# Uptime Kuma is WebSocket-based, so confirm the tunnel module is present
sudo httpd -M | grep -E 'proxy_http|proxy_wstunnel'Configure Reverse Proxy VirtualHost
Create a new configuration file /etc/httpd/conf.d/uptime-kuma.conf:
<VirtualHost _default_:80>
ServerName your_domain_or_ip
ProxyPreserveHost On
ProxyRequests Off
<Proxy *>
Require all granted
</Proxy>
# 1. Handle Socket.io long-polling & bypass strict ModSecurity rules
<Location /socket.io>
<IfModule mod_security2.c>
SecRuleEngine Off
SecRuleRemoveById 920350
SecRuleRemoveById 920420
</IfModule>
Require all granted
ProxyPass http://127.0.0.1:3001/socket.io
ProxyPassReverse http://127.0.0.1:3001/socket.io
</Location>
# 2. Upgrade HTTP polling connections to persistent WebSocket channel
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/socket.io/(.*) ws://127.0.0.1:3001/socket.io/$1 [P,L]
# 3. Proxy routing for regular frontend assets
ProxyPass / http://127.0.0.1:3001/
ProxyPassReverse / http://127.0.0.1:3001/
# Log configuration
ErrorLog /var/log/httpd/uptime-kuma-error.log
CustomLog /var/log/httpd/uptime-kuma-access.log combined
</VirtualHost>Warning
Unlike most web apps, Uptime Kuma cannot work through a proxy that drops WebSocket upgrades — the dashboard loads but every monitor stays disconnected. Both the
Upgrade/Connectionconditions above and a loadedmod_proxy_wstunnelare required.
4. Initialize Services & Browser Setup
Validate Apache Configuration
sudo httpd -t
# Output should show: Syntax OKStart Apache Service
sudo systemctl restart httpdClear Browser Storage (Fix 403 Forbidden Loop)
If you previously accessed the IP and encountered a 403 Forbidden error, the browser may cache old socket session tokens:
- Open your browser to
http://<YOUR_SERVER_IP> - Press F12 to open Developer Tools.
- Go to the Application (Chrome/Edge/Brave) or Storage (Firefox) tab.
- Select Clear Site Data (or clear Cookies, Local Storage, and Session Storage for the server's IP).
- Perform a hard refresh: Ctrl + F5 (Windows/Linux) or Cmd + Shift + R (Mac).
Create your administrator credentials on the Uptime Kuma dashboard setup screen.
Need Help with Your Infrastructure?
If you're looking to implement a similar monitoring setup, configure secure reverse proxies, or need a robust DevOps solution, feel free to reach out!
Contact Me: