{"id":3307,"date":"2026-06-07T06:51:44","date_gmt":"2026-06-07T06:51:44","guid":{"rendered":"https:\/\/muhammadsoliman.com\/?p=3307"},"modified":"2026-06-07T07:01:10","modified_gmt":"2026-06-07T07:01:10","slug":"build-private-uptime-kuma-monitoring-server-proxmox-docker","status":"publish","type":"post","link":"https:\/\/muhammadsoliman.com\/index.php\/2026\/06\/07\/build-private-uptime-kuma-monitoring-server-proxmox-docker\/","title":{"rendered":"How to Build a Private Uptime Kuma Monitoring Server with Docker and Proxmox"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When you start self-hosting more than one service, monitoring becomes very important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At first, it is easy to check everything manually. You open your website, check your dashboard, SSH into the server, run a few commands, and hope everything is fine.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But once you have multiple services running, manual checking is not enough.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I wanted a simple way to know:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Is my video server online?\nIs my reverse proxy working?\nIs my admin dashboard protected?\nAre my virtual machines reachable?\nIs my monitoring server itself healthy?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For this, I built a dedicated private monitoring server using <strong>Uptime Kuma<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This article explains what Uptime Kuma is, why I installed it on a separate VM, how the architecture works, and the main lessons I learned while building it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What Is Uptime Kuma?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Uptime Kuma<\/strong> is an open-source monitoring tool that lets you watch websites, servers, APIs, ports, and internal services from a clean web dashboard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It can monitor things like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTPS websites\nInternal HTTP services\nPing checks\nTCP ports\nDocker services\nSSL certificate status\nLogin-protected dashboards\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It gives you a visual dashboard showing what is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UP\nDOWN\nPending\nSlow\nUnavailable\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It can also send alerts later through email, Telegram, Discord, Slack, webhooks, and other notification channels.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In simple words, Uptime Kuma is like a watchman for your server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of wondering whether your services are working, you open one dashboard and see the status clearly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why I Used a Separate VM for Monitoring<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I did not want to install monitoring directly on the Proxmox host.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Proxmox host should stay clean. It should only run virtualization, storage, networking, and core host services.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So I created a separate virtual machine just for monitoring.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example architecture:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Proxmox Host\n\u2502\n\u251c\u2500\u2500 VM 100 \u2014 Docker \/ Traefik \/ Portainer\n\u251c\u2500\u2500 VM 101 \u2014 Video meeting server\n\u2514\u2500\u2500 VM 102 \u2014 Monitoring \/ Uptime Kuma\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This separation is useful because the monitoring VM can watch the other VMs without mixing workloads.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The monitoring VM stays lightweight and focused.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It does not run video meetings.<br>It does not run the reverse proxy.<br>It does not run the main application stack.<br>It only monitors.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">My Monitoring VM Design<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For the monitoring VM, I used a small Debian server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example VM specification:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>OS: Debian 12\nCPU: 2 vCPU\nRAM: 4 GB\nDisk: 80 GB\nNetwork: private bridge\nAccess: private only\nMonitoring app: Uptime Kuma\nRuntime: Docker + Docker Compose\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is more than enough for a small monitoring dashboard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal was not to build a heavy server. The goal was to build a reliable control point that can monitor other services and later help with backup checks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why I Kept Uptime Kuma Private<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One important decision: I did <strong>not<\/strong> expose Uptime Kuma publicly at first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The dashboard was only reachable through the private server network.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Uptime Kuma private address:\nhttp:&#47;&#47;10.0.10.20:3001\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">From my computer, I accessed it through an SSH tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh -N -L 13001:10.0.10.20:3001 user@proxmox.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then I opened:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:13001\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This gave me secure access without creating a public dashboard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is the safest starting point.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Later, it is possible to expose the dashboard through a protected subdomain like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>monitor.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But I recommend doing that only after adding strong protection, such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Reverse proxy basic authentication\nUptime Kuma login\nVPN or IP allowlist\nStrong passwords\nHTTPS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A monitoring dashboard tells a lot about your infrastructure. Do not expose it casually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Installing Uptime Kuma with Docker Compose<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The basic Docker Compose idea is simple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example folder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/monitoring\/uptime-kuma\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example <code>compose.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>services:\n  uptime-kuma:\n    image: louislam\/uptime-kuma:2\n    container_name: uptime-kuma\n    restart: unless-stopped\n    volumes:\n      - .\/data:\/app\/data\n    ports:\n      - \"10.0.10.20:3001:3001\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A few important details:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The data folder is very important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/data:\/app\/data\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That folder stores:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Dashboard users\nMonitor settings\nHistory\nNotification settings\nDatabase files\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not delete it unless you intentionally want to reset Uptime Kuma.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start it with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose up -d\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check it with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose ps\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A healthy container should show something like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uptime-kuma   Up   healthy\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The First Setup Page<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After the container starts, open the dashboard through your private access method.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:13001\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On first launch, Uptime Kuma asks you to create an admin account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use a strong password and save it safely.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a simple self-hosted setup, the default SQLite option is fine.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The First Monitors I Added<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After the dashboard was ready, I added basic monitors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example monitors:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Main Public Website\nInternal Application Web Check\nReverse Proxy Auth Check\nAdmin Dashboard Auth Check\nVM 100 Ping\nVM 101 Ping\nVM 102 Ping\nMonitoring Self Check\nPrivate Gateway Ping\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For HTTP checks, Uptime Kuma can verify status codes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, a normal public website should return:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>200 OK\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But a protected dashboard may return:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>401 Unauthorized\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That can actually be a good result.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a dashboard is supposed to be protected by basic authentication, then <code>401 Unauthorized<\/code> means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>The service is reachable\nThe protection layer is working\nThe dashboard is not openly exposed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So in Uptime Kuma, I created some monitors where the accepted status code is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>401\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is useful for checking protected admin tools.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Hairpin NAT Problem<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One issue I ran into was internal monitoring of public domains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From outside the server, a domain like this may work:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;app.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But from inside a private VM, the same domain may resolve to the public server IP and fail.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is often because of hairpin NAT or loopback NAT.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In simple terms:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Private VM\n\u2192 public domain\n\u2192 public IP of same server\n\u2192 tries to loop back inside\n\u2192 connection fails\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The service was not actually down. The private monitoring VM just could not reach it using the public path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To solve this, I forced the Uptime Kuma container to resolve certain public service domains to the private reverse proxy IP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extra_hosts:\n  - \"app.example.com:10.0.10.10\"\n  - \"dashboard.example.com:10.0.10.10\"\n  - \"admin.example.com:10.0.10.10\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After recreating the container, Uptime Kuma could monitor those domains through the internal network while still using the real HTTPS hostname.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is a very useful trick for private monitoring behind a reverse proxy.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Example Final Compose File<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a generalized example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>services:\n  uptime-kuma:\n    image: louislam\/uptime-kuma:2\n    container_name: uptime-kuma\n    restart: unless-stopped\n    volumes:\n      - .\/data:\/app\/data\n    ports:\n      - \"10.0.10.20:3001:3001\"\n    extra_hosts:\n      - \"app.example.com:10.0.10.10\"\n      - \"reverse-proxy.example.com:10.0.10.10\"\n      - \"dashboard.example.com:10.0.10.10\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>10.0.10.20 = monitoring VM\n10.0.10.10 = reverse proxy VM\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use your own private IPs and domains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not publish private details in public tutorials.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Useful Maintenance Commands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Check the container:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/opt\/monitoring\/uptime-kuma\ndocker compose ps\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">View logs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker logs --tail 50 uptime-kuma\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Restart Uptime Kuma:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/opt\/monitoring\/uptime-kuma\ndocker compose restart\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Update Uptime Kuma:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/opt\/monitoring\/uptime-kuma\ndocker compose pull\ndocker compose up -d\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test the private web port:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I http:\/\/10.0.10.20:3001\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test a monitored domain from inside the container:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec uptime-kuma curl -Ik https:\/\/app.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check whether a domain resolves correctly inside the container:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker exec uptime-kuma getent hosts app.example.com\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why I Took Snapshots During the Build<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before major changes, I took Proxmox snapshots.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example snapshot stages:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>clean-debian-base\ndocker-installed-before-monitoring\nuptime-kuma-working-private\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This made the build safer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If something broke, I could roll back to a known good point.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, snapshots are not the same as real backups.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A snapshot stored on the same server is useful for rollback, but it does not protect you if the whole server or storage pool fails.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For real disaster recovery, you still need off-server backups.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A better future backup plan would include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Proxmox VM backups\nOff-server storage\nBackup verification\nBackup pruning\nBackup success\/failure monitoring\nAlert notifications\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What Uptime Kuma Does Not Replace<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uptime Kuma is excellent for availability monitoring, but it does not replace everything.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It does not replace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Full server backups\nSecurity hardening\nFirewall rules\nLog management\nIntrusion detection\nDisaster recovery planning\nApplication-level debugging\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It tells you when something is down or unhealthy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then you still need good maintenance practices to fix the problem.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Should Uptime Kuma Be Public?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My recommendation: start private.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use SSH tunnel or VPN first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Expose it publicly only if you really need it, and only with multiple protection layers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A safe future setup might look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;monitor.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Protected by:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTPS\nReverse proxy authentication\nUptime Kuma login\nVPN or IP allowlist\nStrong admin password\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For public visitors, use a separate public status page instead of exposing the admin dashboard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;status.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That page can show limited public information without exposing the full monitoring admin panel.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Best Services to Monitor First<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start simple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Good first monitors:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Main website HTTP check\nInternal app HTTP check\nReverse proxy dashboard auth check\nAdmin dashboard auth check\nMain VM ping checks\nMonitoring server self-check\nPrivate gateway ping\nSSL certificate check\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Later, add more advanced checks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Backup success\/failure check\nDisk usage check\nDatabase health check\nDocker container check\nVideo service internal check\nTURN\/STUN service check\nAPI endpoint check\nNotification delivery test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not try to monitor everything on day one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Build a clean foundation first.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Lessons Learned<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The biggest lessons from this build:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Keep monitoring separate from main services.\nDo not install monitoring on the Proxmox host.\nKeep the dashboard private at first.\nUse Docker Compose for clean deployment.\nTake snapshots before major changes.\nDo not confuse snapshots with real backups.\nUse 401 Unauthorized as a healthy result for protected dashboards.\nWatch out for hairpin NAT when monitoring public domains from inside a private network.\nAdd alerts after the basic dashboard is stable.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uptime Kuma is one of the easiest and most useful tools to add to a self-hosted infrastructure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It gives you visibility.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of guessing whether your services are working, you can see the status in one place.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For my setup, the best approach was:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Dedicated monitoring VM\nDebian server\nDocker Compose\nPrivate Uptime Kuma dashboard\nSSH tunnel access\nInternal service checks\nProtected dashboard checks\nSnapshots before major changes\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The next important step is alerting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A dashboard is helpful, but alerts are what tell you something is wrong when you are not looking.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For production, I would add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Telegram alerts\nEmail alerts\nBackup monitoring\nDisk usage checks\nOff-server backup verification\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Monitoring is not the end of server maintenance, but it is one of the best foundations you can build early.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">FAQ<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">What is Uptime Kuma used for?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uptime Kuma is used to monitor websites, servers, APIs, ports, and internal services. It shows whether each service is up or down and can send alerts when something fails.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Is Uptime Kuma free?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Uptime Kuma is an open-source self-hosted monitoring tool.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Can Uptime Kuma monitor private services?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. If Uptime Kuma runs inside your private network, it can monitor private IP addresses, internal services, and private dashboards.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Should I expose Uptime Kuma to the internet?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It is better to keep it private at first. Use an SSH tunnel or VPN. If you expose it later, protect it with HTTPS, reverse proxy authentication, Uptime Kuma login, and preferably VPN or IP allowlisting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Can Uptime Kuma monitor Proxmox?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uptime Kuma can monitor Proxmox-related services indirectly through ping checks, HTTPS checks, port checks, and custom scripts. It is not a full Proxmox management replacement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why does my public domain fail from inside my server?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This can happen because of hairpin NAT or loopback NAT. The internal VM resolves the public domain to the public IP, then fails to loop back into the same server. One solution is to use internal DNS or Docker <code>extra_hosts<\/code> to point that domain to the private reverse proxy IP.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Is a Proxmox snapshot a backup?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">No. A snapshot is a local rollback point. A real backup should be copied off-server to separate storage.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What should I monitor first?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start with your main website, reverse proxy, key internal services, VM ping checks, the monitoring server itself, and protected dashboard checks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you start self-hosting more than one service, monitoring becomes very important. At first, it is&#8230;<\/p>\n","protected":false},"author":1,"featured_media":3310,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"above","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[31,18,32,30],"tags":[],"class_list":["post-3307","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-linux-server","category-proxmox","category-server"],"taxonomy_info":{"category":[{"value":31,"label":"Linux"},{"value":18,"label":"linux server"},{"value":32,"label":"proxmox"},{"value":30,"label":"Server"}]},"featured_image_src_large":["https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/06\/ChatGPT-Image-Jun-7-2026-01_39_50-AM-1024x768.png",1024,768,true],"author_info":{"display_name":"Muhammad Soliman","author_link":"https:\/\/muhammadsoliman.com\/author\/muhmmad-soliman\/"},"comment_info":0,"category_info":[{"term_id":31,"name":"Linux","slug":"linux","term_group":0,"term_taxonomy_id":31,"taxonomy":"category","description":"","parent":0,"count":3,"filter":"raw","cat_ID":31,"category_count":3,"category_description":"","cat_name":"Linux","category_nicename":"linux","category_parent":0},{"term_id":18,"name":"linux server","slug":"linux-server","term_group":0,"term_taxonomy_id":18,"taxonomy":"category","description":"","parent":0,"count":3,"filter":"raw","cat_ID":18,"category_count":3,"category_description":"","cat_name":"linux server","category_nicename":"linux-server","category_parent":0},{"term_id":32,"name":"proxmox","slug":"proxmox","term_group":0,"term_taxonomy_id":32,"taxonomy":"category","description":"","parent":0,"count":2,"filter":"raw","cat_ID":32,"category_count":2,"category_description":"","cat_name":"proxmox","category_nicename":"proxmox","category_parent":0},{"term_id":30,"name":"Server","slug":"server","term_group":0,"term_taxonomy_id":30,"taxonomy":"category","description":"","parent":0,"count":3,"filter":"raw","cat_ID":30,"category_count":3,"category_description":"","cat_name":"Server","category_nicename":"server","category_parent":0}],"tag_info":false,"_links":{"self":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/comments?post=3307"}],"version-history":[{"count":4,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3307\/revisions"}],"predecessor-version":[{"id":3312,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3307\/revisions\/3312"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/media\/3310"}],"wp:attachment":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/media?parent=3307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/categories?post=3307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/tags?post=3307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}