<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>DreamServer Blog - DreamServer</title><link>https://dreamserver.ro/en/blog/</link><description>Technical articles, tutorials and announcements from DreamServer - dedicated servers, VDS, networking, applications and platforms, plus promotions and new features. on DreamServer</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 24 Aug 2026 01:30:00 +0300</lastBuildDate><atom:link href="https://dreamserver.ro/en/blog/" rel="self" type="application/rss+xml"/><item><title>Last Summer Discount: blade servers up to 60% off until September 1</title><link>https://dreamserver.ro/en/blog/last-summer-discount-blade-servers/</link><pubDate>Mon, 24 Aug 2026 01:30:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/last-summer-discount-blade-servers/</guid><description>Summer is almost over and so is the price. Until September 1 our HP ProLiant blade servers are up to 60% off: 192 GB RAM minimum, dual Xeon, perfect for virtualization, from 100 EUR / month in Bucharest.</description><content:encoded><![CDATA[<p>Summer is almost over, and so is the price. For the last week of August we are running our <strong>Last Summer Discount</strong> on HP ProLiant blade servers: real dedicated hardware, in our Bucharest data center, at up to <strong>60% off</strong> the regular price. The offer runs until <strong>September 1</strong>, then prices go back up, so this is genuinely the last week to grab it.</p>
<h2 id="what-is-on-sale">What is on sale</h2>
<p>Three blade servers, each a proper dual-CPU machine with a generous amount of ECC RAM, the kind of box you actually want for virtualization, containers or a busy database:</p>
<table>
  <thead>
      <tr>
          <th>Server</th>
          <th>CPU</th>
          <th>RAM</th>
          <th>Summer price</th>
          <th>Regular</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>HP BL460c G6</td>
          <td>Dual Xeon L5640</td>
          <td>192 GB</td>
          <td><strong>€100 / 500 lei /mo</strong></td>
          <td><del>€230</del>  −57%</td>
      </tr>
      <tr>
          <td>HP BL460c G7</td>
          <td>Dual Xeon L5640</td>
          <td>192 GB</td>
          <td><strong>€100 / 500 lei /mo</strong></td>
          <td><del>€250</del>  −60%</td>
      </tr>
      <tr>
          <td>HP BL490c G7</td>
          <td>Dual Xeon E5645</td>
          <td>288 GB</td>
          <td><strong>€150 / 750 lei /mo</strong></td>
          <td><del>€275</del>  −45%</td>
      </tr>
  </tbody>
</table>
<p>Every one of them ships with <strong>192 GB of ECC RAM minimum</strong> (288 GB on the BL490c), 24 cores across two Xeons, up to 2x SAS/SATA drives with RAID 0 or 1, dual 1 or 10 Gbit/s networking, <strong>25 TB of monthly traffic</strong> and <strong>10 IPv4 + 10 IPv6</strong> addresses. All hosted in Bucharest on our own AS57050 network, with free peering at DSIX.</p>
<h2 id="why-all-that-ram-matters">Why all that RAM matters</h2>
<p>192 GB is a lot of memory for the money, and memory is exactly what you run out of first when you virtualize. On one of these blades you can comfortably run a dozen or more VMs or LXC containers, a Proxmox lab, a Kubernetes node pool, or a memory-hungry database, without the constant &ldquo;out of RAM&rdquo; juggling you get on smaller boxes. Dedicated cores, dedicated RAM, no noisy neighbours.</p>
<p>They are also a bargain per gigabyte: at 100 EUR for 192 GB of ECC RAM plus 24 cores, the summer price works out to well under a euro per gigabyte of real, dedicated memory.</p>
<h2 id="the-only-catch-it-ends-september-1">The only catch: it ends September 1</h2>
<p>There is no catch on the hardware, only on the clock. The discount is live now and disappears on <strong>September 1</strong>. Order before then and the price is <strong>locked for the whole term</strong> of your service: it does not jump back up at renewal.</p>
<p><a href="/en/dedicated-servers/bladeservers/">See the blade servers →</a>
</p>
<p>Enjoy the last days of summer, and let one of these blades do the heavy lifting while you are at the beach.</p>
]]></content:encoded><category>Announcements</category><category>Dedicated Servers</category></item><item><title>RoPay is here: pay for your hosting instantly by QR, with a 0.5% fee</title><link>https://dreamserver.ro/en/blog/ropay-instant-bank-payments/</link><pubDate>Fri, 14 Aug 2026 10:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/ropay-instant-bank-payments/</guid><description>Starting today you can pay your DreamServer invoices with RoPay: instant bank-to-bank payments by QR code, straight from your banking app, no card needed, with a low 0.5% fee. Here is how it works.</description><content:encoded><![CDATA[<p>Paying for your hosting should be as quick as scanning a QR code with your phone. Starting today, at DreamServer, it is. We have added <strong>RoPay</strong> as a payment method, so you can settle any invoice straight from your banking app, no card numbers, no redirects to a card page, no waiting. The money moves bank to bank in seconds, and the fee is a flat <strong>0.5%</strong>.</p>
<p>This is a short, practical post: what RoPay is, why we like it, and exactly how you pay with it, step by step.</p>
<h2 id="what-ropay-is">What RoPay is</h2>
<p>RoPay is Romania&rsquo;s instant account-to-account payment scheme. Instead of typing a card number, you initiate a payment by scanning a <strong>QR code</strong> (or tapping a link on your phone), and you approve it inside your own bank&rsquo;s mobile app with your usual authentication, fingerprint, face or PIN. The transfer settles in real time, in seconds, directly from your bank account to ours. No card network in the middle, no card details shared with anyone.</p>
<p>We integrated RoPay through <a href="https://www.librabank.ro/" rel="nofollow">Libra Internet Bank</a>
, our partner bank on the scheme. For you as a customer, the bank on our side does not matter: what matters is that <strong>your</strong> bank is one of the Romanian banks enrolled in RoPay, which by now is almost all of them.</p>
<h3 id="why-we-added-it">Why we added it</h3>
<p>Three honest reasons:</p>
<ul>
<li><strong>It is instant.</strong> The invoice is marked paid the moment you confirm in your app, so your service is provisioned or renewed without the &ldquo;waiting for the payment to clear&rdquo; limbo.</li>
<li><strong>It is safer.</strong> You approve the exact amount and merchant inside your trusted banking app. You never type card details into a payment page, so there is nothing to skim or leak.</li>
<li><strong>It is cheaper, and we pass that on.</strong> Card processing carries hefty fees. RoPay does not. Paying by RoPay costs a flat <strong>0.5%</strong>, noticeably less than a card, which is money that stays in your pocket.</li>
</ul>
<h2 id="how-to-pay-with-ropay-step-by-step">How to pay with RoPay, step by step</h2>
<ol>
<li><strong>Open your invoice</strong> in the DreamServer client area and choose <strong>RoPay</strong> as the payment method, then click pay.</li>
<li><strong>A payment page appears</strong> showing the amount in RON and a <strong>RoPay QR code</strong>.</li>
<li><strong>Open your bank&rsquo;s mobile app</strong> on your phone, find its &ldquo;scan QR to pay&rdquo; or RoPay feature, and <strong>scan the code</strong>. If you are already on your phone, just tap the <strong>&ldquo;Open in app&rdquo;</strong> button and it will jump straight into your banking app.</li>
<li><strong>Check and confirm.</strong> Your app shows the amount and that the payment goes to DreamServer. Approve it the way you always do, fingerprint, face or PIN.</li>
<li><strong>Done.</strong> The payment lands in seconds, the page confirms it automatically, and your invoice is marked paid. No screenshots to send us, no &ldquo;please confirm my payment&rdquo; ticket.</li>
</ol>
<p>That is the whole flow. The first time takes a curious extra ten seconds while you find the QR feature in your app; after that it is faster than reaching for your wallet.</p>
<h2 id="good-to-know">Good to know</h2>
<p>A few honest details so there are no surprises:</p>
<ul>
<li><strong>RoPay works in RON only.</strong> It is a Romanian domestic instant-payment scheme, so it settles in lei. If your invoice happens to be issued in EUR or USD, the RoPay page will tell you and offer to open a ticket. Just ask and we will reissue the invoice in RON so you can pay it with RoPay.</li>
<li><strong>You need RoPay active in your banking app.</strong> You need a Romanian bank account at a RoPay-enrolled bank, with the feature switched on. Most Romanian banking apps have it under names like &ldquo;instant payments&rdquo;, &ldquo;RoPay&rdquo; or &ldquo;pay by QR&rdquo;.</li>
<li><strong>Not sure how your bank does it?</strong> Open your banking app and look for a &ldquo;scan to pay&rdquo;, &ldquo;QR&rdquo; or &ldquo;RoPay&rdquo; option, usually near transfers or payments. If you cannot find it, your bank may ask you to enroll or activate RoPay once, a one-minute, one-time step that links your account to the scheme. After that, every future payment is just scan and confirm.</li>
<li><strong>The QR is single-use.</strong> Each payment generates a fresh QR, so if you leave the page and come back, you will get a new code. That is by design, for security.</li>
</ul>
<h2 id="try-it-on-your-next-invoice">Try it on your next invoice</h2>
<p>RoPay sits right next to our existing payment methods, so nothing you already use goes away, you simply have a faster, cheaper option now. Next time an invoice is due, pick RoPay, scan, confirm, and get on with your day.</p>
<p>If you are new here and want something to pay for, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 are a good place to start, and if you would rather we run the server for you, our <a href="/en/services/server-administration/">server administration service</a>
 has you covered. Either way, when the invoice arrives, RoPay will be waiting, one scan away.</p>
]]></content:encoded><category>Announcements</category><category>Payments</category></item><item><title>Warpgate: the open-source bastion that brings order to your SSH access</title><link>https://dreamserver.ro/en/blog/warpgate-bastion-ssh-open-source/</link><pubDate>Sun, 26 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/warpgate-bastion-ssh-open-source/</guid><description>A hands-on look at Warpgate, the self-hosted PAM bastion written in Rust: a transparent proxy for SSH, HTTPS, MySQL, Postgres and Kubernetes, 2FA and SSO out of the box, session recording, install on a VPS in minutes, and the limits to know before production.</description><content:encoded><![CDATA[<p>There is a moment, in the life of any infrastructure that grows, when the answer to &ldquo;who has access to this server?&rdquo; turns into a shrug. SSH keys have multiplied like rabbits. The <code>authorized_keys</code> file on ten machines holds keys nobody recognises anymore. A former contractor technically still has access to the production database, and nobody is entirely sure their key was revoked everywhere. When someone asks &ldquo;what did user X do on server Y last week?&rdquo;, the honest answer is: no idea.</p>
<p>This is where a bastion earns its keep. And <a href="https://github.com/warp-tech/warpgate" rel="nofollow">Warpgate</a>
, the open-source bastion written in Rust by the <a href="https://warpgate.null.page/" rel="nofollow">warp-tech</a>
 team, solves exactly that pain without asking you to reinvent your whole access story. We have been testing it in front of a handful of servers in our Bucharest datacenter for a few weeks. This post is the field report.</p>
<h2 id="what-warpgate-actually-is">What Warpgate actually is</h2>
<p>In short: Warpgate is a <strong>transparent proxy that sits between you and your internal infrastructure</strong> and needs absolutely no client-side software. You install no agent, you run no wrapper around <code>ssh</code>, you force no new tool on the team. You keep running <code>ssh</code>, opening VSCode Remote, pasting a MySQL connection string into your favourite client, logging into a web panel from the browser. Warpgate slots itself onto the path, authenticates, applies the access policy, and only then hands the connection off to the real target, all while recording a live session for audit.</p>
<p>The &ldquo;transparent&rdquo; part is the crux and worth underlining. Warpgate is <strong>not</strong> a classic jump host, where you log into an intermediate box and then fire a second <code>ssh</code> at the destination. The difference is subtle but important: from a jump host you take two hops, you need keys on the middle box, you juggle <code>ProxyJump</code> and agent forwarding. Warpgate finishes authentication in front of you and then hands off to the server in a way that is fully transparent to the client. From your point of view, you run a single command and you are on the server. From a security point of view, every byte that flows was inspected, attributed to an identity, and saved.</p>
<h3 id="why-being-written-in-rust-actually-matters">Why being written in Rust actually matters</h3>
<p>It may sound like a fan detail, but it has practical consequences. Warpgate is <strong>100% safe Rust</strong>, with its own SSH implementation (the <code>russh</code> library), a web plane on the <code>poem</code> framework, and state persisted to SQLite through <code>sea-orm</code> and <code>sqlx</code>. The admin interface is a modern Svelte 5 frontend.</p>
<p>What this means operationally: a single binary, no external dependencies, no heavy runtime to patch, and none of the memory-safety vulnerability class (buffer overflows, use-after-free) that haunted network software written in C for three decades. For a service that by definition sits exposed at the edge of your network and is the first to see every access attempt, &ldquo;safe by construction&rdquo; is not a marketing slogan, it is exactly the property you want.</p>
<h2 id="which-protocols-it-covers">Which protocols it covers</h2>
<p>Warpgate started life as an SSH bastion, but it has grown a lot. As of today it exposes <strong>native listeners</strong> for:</p>
<ul>
<li><strong>SSH</strong>, the classic case: shell access to servers, with full session recording.</li>
<li><strong>HTTPS</strong>, to put any internal web application (an admin panel, a Grafana, a router) behind the same authentication gate.</li>
<li><strong>MySQL</strong> and <strong>PostgreSQL</strong>, so database connections also flow through the bastion, with the same identity and audit model.</li>
<li><strong>Kubernetes</strong>, for <code>kubectl</code> access to clusters through the same control point.</li>
<li><strong>RDP</strong> and <strong>VNC</strong>, added more recently, for remote desktop sessions.</li>
</ul>
<p>All through the same binary, the same admin UI, the same user and role model. You are not juggling six different tools; you have a single place where you say who reaches what.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The open-source edition, which is the only edition, is generous. Warpgate is licensed under Apache-2.0, has more than 7,400 stars on GitHub, and is actively developed. Out of the box you get:</p>
<ul>
<li><strong>Native two-factor authentication (2FA)</strong> via TOTP. That is the code generator from Google Authenticator, Aegis or any other app, with no SMS gateway to pay for.</li>
<li><strong>Single Sign-On through OpenID Connect</strong>, to tie Warpgate to your identity provider (Authelia, Keycloak, Google Workspace, Azure AD). A user logs in once, with the company identity, and gets exactly the access you assigned them.</li>
<li><strong>Full session recording</strong>, with live viewing and later replay directly from the admin UI. You can watch in real time what is being typed in an SSH session, or come back two weeks later and replay exactly what happened.</li>
<li><strong>Command-level audit</strong>, not just &ldquo;the user connected at 14:32&rdquo; but what they ran there.</li>
<li><strong>Precise access control</strong>, with fine-grained assignment between users, roles and targets. A user sees and touches only the services assigned to them, nothing else.</li>
<li><strong>Brute-force protection</strong>, with IP blocking and user lockout, so nobody turns your port 2222 into a playground.</li>
<li><strong>A single binary, no dependencies</strong>, that boots in seconds and moves easily from one place to another.</li>
</ul>
<p>What it does not do, and it is worth being clear: Warpgate is not a VPN and it is not a secrets vault. It does not encrypt your whole network segment end to end for all traffic; it gives you a controlled access gate to specific services. It does not replace a HashiCorp Vault for secret rotation. It is one piece of an access strategy, not the whole strategy.</p>
<h2 id="installing-warpgate-on-a-vps">Installing Warpgate on a VPS</h2>
<p>This is the part where many &ldquo;self-hosted PAM&rdquo; projects make you regret the decision, burying you under a three-hundred-line helm chart and five external dependencies. Warpgate genuinely does not. Being a single Rust binary, the install is almost embarrassingly simple.</p>
<p>Sane starting resources: <strong>1 vCPU and 1 GB RAM</strong> comfortably run a bastion for a small team; step up to 2 vCPU / 2 GB if you record many concurrent sessions or push database traffic. SQLite keeps state locally, so you need no separate database server.</p>
<p>On a fresh Debian or Ubuntu server, grab the binary from the official releases:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ver<span style="color:#f92672">=</span>v0.26.1
</span></span><span style="display:flex;"><span>wget https://github.com/warp-tech/warpgate/releases/download/$ver/warpgate-$ver-x86_64-linux
</span></span><span style="display:flex;"><span>chmod +x warpgate-$ver-x86_64-linux
</span></span><span style="display:flex;"><span>sudo mv warpgate-$ver-x86_64-linux /usr/local/bin/warpgate
</span></span></code></pre></div><p>Then run the interactive setup, which generates your config file, the host keys and the first admin account:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo warpgate --config /etc/warpgate.yaml setup
</span></span></code></pre></div><p>Setup asks which ports each protocol should listen on. The defaults are chosen well so they do not clash with services already on the box:</p>
<ul>
<li><strong>SSH</strong>: port <code>2222</code> (not 22, so it does not collide with the host&rsquo;s sshd).</li>
<li><strong>HTTPS admin UI</strong>: port <code>8888</code>.</li>
<li><strong>MySQL</strong>: port <code>33306</code>.</li>
</ul>
<p>Data and session history land in a SQLite database under <code>/var/lib/warpgate</code>. After setup, start the service:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo warpgate --config /etc/warpgate.yaml run
</span></span></code></pre></div><p>For production you obviously wrap it in a systemd unit with <code>Restart=always</code>, so it comes back up after a reboot. If you prefer containers, there is an official Docker image plus a helm chart, but for a single-node bastion the binary straight on the host is the cleanest option: fewer network layers between an attacker and your logs.</p>
<p>One hygiene step you never skip: <strong>the admin UI on 8888 must not be exposed to the whole world.</strong> Restrict it to your management IPs with a firewall rule:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 2222/tcp
</span></span><span style="display:flex;"><span>ufw allow from YOUR_OFFICE_IP to any port <span style="color:#ae81ff">8888</span> proto tcp
</span></span><span style="display:flex;"><span>ufw deny 8888/tcp
</span></span></code></pre></div><p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, the same rule belongs in your provider&rsquo;s network firewall too, do not rely on host-level rules alone for management interfaces.</p>
<h2 id="adding-a-target-and-a-user">Adding a target and a user</h2>
<p>The mental model takes a minute to click, but after that it is clear. Warpgate has three entities that combine: <strong>targets</strong> (the servers and services you want to grant access to), <strong>users</strong> (the people who connect) and <strong>roles</strong> (groups that bind users to targets).</p>
<p>In the admin UI at <code>https://&lt;your-server-ip&gt;:8888/</code>, you first add an SSH target: its name inside Warpgate (say <code>web-prod</code>), the real address and port of the origin (<code>10.0.0.5:22</code>), and how Warpgate authenticates onward to the server, with a dedicated SSH key you generate for the bastion. That key goes into <code>authorized_keys</code> on the target, and it is the only one that needs to live there: from that moment, people no longer keep personal keys scattered across servers, they all go through the bastion.</p>
<p>Then you create a user, enable 2FA (or tie them through OIDC to the company identity provider), and assign them to a role that has access to <code>web-prod</code>. Done. The user connects like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ssh admin:web-prod@bastion.example.com -p <span style="color:#ae81ff">2222</span>
</span></span></code></pre></div><p>The <code>user:target@host</code> syntax is the whole trick: the part before the <code>:</code> is the Warpgate identity, the part after is the name of the target you want to reach. Warpgate authenticates you (password plus TOTP code, or an OIDC redirect in the browser), checks that you are allowed on <code>web-prod</code>, and drops you on the server. For databases, same model, the syntax just differs slightly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mysql -u <span style="color:#e6db74">&#39;admin#web-db&#39;</span> --host bastion.example.com --port <span style="color:#ae81ff">33306</span> --ssl -p
</span></span></code></pre></div><p>For HTTPS targets, you open the Warpgate UI in the browser, authenticate, and click the target: the bastion proxies the internal web application through its authenticated gate, without exposing the application directly to the internet.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few things we found genuinely useful beyond the defaults.</p>
<h3 id="enforce-2fa-for-everyone-from-day-one">Enforce 2FA for everyone, from day one</h3>
<p>A bastion without a second factor is just SSH with extra steps. Turn on mandatory TOTP for all users on the first day, before anyone gets used to living without it. The cost is a QR-code scan; the benefit is that a stolen password alone no longer opens anything.</p>
<h3 id="tie-it-to-oidc-and-skip-manual-account-management">Tie it to OIDC and skip manual account management</h3>
<p>If you already run an identity provider, OIDC integration turns Warpgate from &ldquo;yet another place I keep passwords&rdquo; into &ldquo;yet another service that respects the company identity&rdquo;. When a person leaves, you disable their account in one place, the IdP, and their access to everything that flows through the bastion vanishes instantly. That alone solves the scenario we opened this article with.</p>
<h3 id="review-the-recordings-do-not-just-let-them-sit">Review the recordings, do not just let them sit</h3>
<p>The real power of session recording is not that it exists, it is that you use it. Once a week, go into <strong>Sessions</strong> in the UI and walk through what happened on the sensitive targets. You will catch things: someone who ran a <code>DROP</code> on the wrong database, a contractor who touched a server that was not their job, an incident nobody reported. Replay is something you simply cannot get from a text log.</p>
<h3 id="one-target--one-dedicated-bastion-key">One target = one dedicated bastion key</h3>
<p>Do not reuse the same SSH key for all targets. Generate one pair per server, held by Warpgate. If you ever need to pull a server out of the bastion or rotate a key, you do it surgically, without touching access to the rest of the fleet.</p>
<h3 id="treat-the-sqlite-database-as-critical-infrastructure">Treat the SQLite database as critical infrastructure</h3>
<p>All the configuration and all the session history live in <code>/var/lib/warpgate</code>. The container or binary can be rebuilt in a minute; the SQLite database is what you would actually miss. Put it in a real backup story, a <a href="/en/virtual-servers/vps/">Proxmox Backup</a>
, snapshots, your call, and test that you know how to restore it.</p>
<h2 id="where-warpgate-is-not-the-right-answer">Where Warpgate is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not a VPN.</strong> Warpgate gives you controlled access to specific services, not a general network tunnel. If you need a laptop to &ldquo;be on the network&rdquo; for dozens of ad-hoc services, a VPN (or WireGuard) remains the complementary tool, not the replaced one.</li>
<li><strong>Not a secrets vault.</strong> It does not auto-rotate database passwords or inject ephemeral credentials Vault-style. It stores the bastion&rsquo;s own access keys, and that is it.</li>
<li><strong>SQLite has a ceiling.</strong> For a normal-sized team and fleet, SQLite is perfect. For tens of thousands of long-retained sessions or aggressive multi-node HA setups, plan carefully, export and archive old recordings instead of letting the database grow forever.</li>
<li><strong>Self-hosting is not free.</strong> You are running another stateful service that needs backups, OS patching and occasional attention. If your team does not want that, managed bastions exist, but you will pay in a different currency, money and control.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at <a href="https://goteleport.com/" rel="nofollow">Teleport</a>
 (extremely capable, with short-lived certificates and infrastructure access at scale, but much heavier and with key features pushed toward the commercial edition), <a href="https://github.com/tg123/sshpiper" rel="nofollow">sshpiper</a>
 (a pure SSH reverse proxy, minimalist, no UI and no session recording), <a href="https://guacamole.apache.org/" rel="nofollow">Apache Guacamole</a>
 (excellent for RDP/VNC in the browser, but centered on remote desktop rather than the multi-protocol bastion model) and a plain hardened SSH jump host (cheap, familiar, but no real audit and the same key-sprawl problem we set out to solve).</p>
<p>Warpgate ended up being the cleanest fit for &ldquo;I want a real bastion, with 2FA and session recording, in front of a small-to-medium fleet of self-hosted servers, without adopting a whole platform&rdquo;. Your weights might be different.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you manage more than a handful of servers and you have reached the point where you can no longer say for certain &ldquo;who has access to what and what did they do there&rdquo;, Warpgate is worth a Sunday afternoon. The install is genuinely fast, the access model settles into your head quickly, and the first time you replay a recorded session to understand an incident, you will know it has earned a permanent place in your stack.</p>
<p>If you run it in production, treat it as a bastion deserves: minimally exposed, with the admin UI locked to trusted IPs, with 2FA enforced, and with the SQLite database backed up. The bastion is, by definition, the single road to the rest of your infrastructure, so it deserves care to match.</p>
<p>We run Warpgate ourselves in front of a couple of internal services and recommend it to customers who ask how to bring order to their SSH access. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 start at small enough sizes to make the experiment cheap, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install, OIDC integration and tuning if you would rather skip the learning curve.</p>
<p>Either way, kicking the tires on Warpgate is a better way to spend an evening than counting the keys in <code>authorized_keys</code> one more time, hoping you recognise all of them. The code is on GitHub, the install is practically a single command, and you will know within an hour whether it earns its place in front of your infrastructure.</p>
]]></content:encoded><category>Bastion</category><category>Security</category></item><item><title>Domain Monitor: never let a domain die from expiry you didn't see coming</title><link>https://dreamserver.ro/en/blog/domain-monitor-expiry-tracking/</link><pubDate>Wed, 22 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/domain-monitor-expiry-tracking/</guid><description>Domain Monitor is a self-hosted PHP tool that tracks domain expiry, WHOIS data, and sends notifications over 7 channels, so you find out before the domain drops, not after.</description><content:encoded><![CDATA[<p>If you manage more than five or six domains, you already know the moment: a client calls in a panic because their site is down, and the reason is the domain expired three days ago and nobody saw the renewal email from the registrar (which, let&rsquo;s be honest, looks exactly like a phishing attempt and lands in spam constantly). An expired domain isn&rsquo;t just a downed site, it&rsquo;s a window where anyone can re-register it once the grace period runs out, and getting it back afterward is either impossible or expensive.</p>
<p><a href="https://github.com/Hosteroid/domain-monitor" rel="nofollow">Domain Monitor</a>
, created and maintained by Hosteroid, is an open-source, MIT-licensed tool built to solve exactly that: it tracks the expiry of your domains and notifies you early, on a channel you actually check. We evaluated it as an alternative to relying on registrars&rsquo; native notification systems, which vary wildly in reliability from provider to provider.</p>
<h2 id="what-domain-monitor-actually-is">What Domain Monitor actually is</h2>
<p>Domain Monitor is a PHP 8.1+ application, built on its own custom MVC framework (not Laravel or Symfony), with MySQL 5.7+ or MariaDB 10.3+ as the data backend and Composer-managed dependencies. The core idea: add your domains to the app, and a cron job periodically runs WHOIS/RDAP lookups for each one, automatically extracting the expiry date and current registrar. As a domain approaches expiry, the app fires notifications at whatever thresholds you configure (default: 60, 30, 21, 14, 7, 5, 3, 2, and 1 days out).</p>
<p>It supports over 1,400 TLDs, with direct integration against the IANA registry to determine the correct WHOIS/RDAP server for each extension, including a dedicated TLD registry importer.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference from relying on registrar emails is having a single place that aggregates all your domains, no matter how many different registrars they&rsquo;re spread across (a common reality once you&rsquo;ve bought domains over several years or inherited them from clients). And multi-channel notifications (not just email, which gets lost in the noise) mean a Slack, Discord, or Telegram ping can hit you directly when a threshold actually matters, not just at the 60-day mark you ignore because &ldquo;there&rsquo;s still time.&rdquo;</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Unlimited domain management</strong>: add, edit, bulk import, and manual or automatic WHOIS refresh.</li>
<li><strong>Smart WHOIS/RDAP lookups</strong>, with automatic detection of expiry date and current registrar, across 1,400+ TLDs.</li>
<li><strong>Multi-channel notifications</strong>: Email, Telegram, Discord, Slack, Mattermost, Pushover, and custom webhooks.</li>
<li><strong>Notification groups</strong>: organize channels and flexibly assign domains to different groups (useful when different clients or teams own responsibility).</li>
<li><strong>Real-time dashboard</strong> showing the status of every domain and active alerts.</li>
<li><strong>Complete notification history</strong>, with detailed logging.</li>
<li><strong>Cron-based automated monitoring</strong>, with configurable check intervals.</li>
<li><strong>Database-backed session system</strong>, tracking geolocation, ISP, and device, with the ability to remotely terminate any active session instantly.</li>
<li><strong>Randomly generated passwords</strong> at install time, and cryptographically secure &ldquo;Remember Me&rdquo; tokens (30-day duration).</li>
<li><strong>Prepared SQL statements</strong> throughout, as a standard defense against injection.</li>
</ul>
<h2 id="installing-domain-monitor-on-a-vps">Installing Domain Monitor on a VPS</h2>
<p>The project ships dedicated Docker support in the <code>domain-monitor-docker</code> directory of the repo. A typical flow on a fresh VPS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/Hosteroid/domain-monitor.git
</span></span><span style="display:flex;"><span>cd domain-monitor/domain-monitor-docker
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>Once it&rsquo;s up, the app exposes a browser-accessible web installer that automatically creates the database tables, generates the encryption key, populates the <code>.env</code> file, and shows you the generated admin credentials once (write them down immediately).</p>
<p>If you prefer a traditional install without Docker, you&rsquo;ll need PHP 8.1+, MySQL/MariaDB, Composer, a webserver with URL rewriting support (Apache with <code>mod_rewrite</code> or Nginx), and cron access for automation.</p>
<p>On the VPS firewall, only allow what&rsquo;s strictly needed publicly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>The app itself is light on resources (plain PHP, no heavy frontend framework), so an entry-level <a href="/en/virtual-servers/vps/">VPS</a>
 is more than enough, even for a few hundred monitored domains.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="per-client-notification-groups-not-one-global-channel">Per-client notification groups, not one global channel</h3>
<p>If you manage domains for multiple clients or projects, don&rsquo;t dump every alert into a single Slack channel where it gets lost. Use notification groups to route each client&rsquo;s domains to their own channel (a dedicated email, a webhook into their ticketing system, or their own Slack), keeping ownership clear.</p>
<h3 id="risk-adjusted-notification-thresholds">Risk-adjusted notification thresholds</h3>
<p>The default thresholds (60 down to 1 day) are a solid starting point, but tune them per domain category. A critical production domain deserves an extra 90-day threshold plus escalation at the 24-48h mark (a different channel, maybe a phone alert via Pushover), while a test domain can stay on the defaults.</p>
<h3 id="remote-session-termination-for-teams">Remote session termination for teams</h3>
<p>If multiple people have access to the admin panel, use the active sessions feature with geolocation and remote termination to instantly cut off access from a lost or compromised device, instead of waiting for the session to expire naturally.</p>
<h3 id="manual-whois-refresh-after-a-renewal">Manual WHOIS refresh after a renewal</h3>
<p>After renewing a domain directly at the registrar, use the manual refresh in the UI instead of waiting for the next automatic cron cycle, so the dashboard reflects the new expiry date right away and doesn&rsquo;t fire a false alert in the meantime.</p>
<h2 id="where-domain-monitor-is-not-the-right-answer">Where Domain Monitor is not the right answer</h2>
<p>If you only own one or two personal domains, spinning up a PHP app with a dedicated database is overkill, your phone&rsquo;s calendar or a simple recurring reminder does the job just as well. Domain Monitor also doesn&rsquo;t auto-renew domains, it&rsquo;s strictly a monitoring and alerting tool, it doesn&rsquo;t connect to the registrar to renew on your behalf (that stays your responsibility or the registrar&rsquo;s). And if you&rsquo;re already a client of a registrar or hosting provider that includes expiry monitoring in its control panel, check whether that existing coverage is enough before adding another app to administer.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://whoisfreaks.com/" rel="nofollow">WhoisFreaks</a>
 or other commercial WHOIS APIs</strong>: a good fit if you need very high query volume and don&rsquo;t want to run anything yourself, but they come with a recurring cost and third-party dependency.</li>
<li><strong>The registrar&rsquo;s native reminders</strong>: free and already active if all your domains sit with one registrar, but reliability varies a lot, and you get no unified view if your domains are spread across registrars.</li>
<li><strong><a href="https://github.com/domainmod/domainmod" rel="nofollow">DomainMOD</a>
</strong>: another self-hosted tool, more geared toward a full inventory of domains/hosting/SSL rather than pure expiry monitoring.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you manage domains for clients, or simply have accumulated more than you can keep track of in your head, a dedicated expiry-monitoring tool is cheap to run and expensive to skip. As a registrar and LIR ourselves, we know firsthand how often an emergency ticket comes in for exactly this reason, and a unified dashboard with alerts on the right channel costs a lot less than recovering an expired domain.</p>
<p>Want to run it on your own <a href="/en/virtual-servers/vps/">VPS</a>
, or would you rather not deal with the operational side at all? Our <a href="/en/services/server-administration/">server administration</a>
 service covers exactly that. And if you&rsquo;d rather offload .ro renewals entirely, check our <a href="/en/services/ro-domain-registration/">.ro domain registration</a>
 service.</p>
]]></content:encoded><category>Network</category><category>Domains</category></item><item><title>Cronmaster: a decent web UI on top of the crontab you keep forgetting about</title><link>https://dreamserver.ro/en/blog/cronmaster-cron-job-manager/</link><pubDate>Sun, 19 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/cronmaster-cron-job-manager/</guid><description>Cronmaster puts a modern web UI, live logging, and SSO on top of cron jobs, so you stop editing crontab with vi over SSH at 2am.</description><content:encoded><![CDATA[<p>There&rsquo;s a decent chance you have a <code>crontab -l</code> somewhere that reads like an archaeology dig: lines commented out since 2021, a job that runs twice because someone also added it to <code>/etc/cron.d/</code>, and no way to tell if last night&rsquo;s run actually succeeded without digging through <code>/var/log/syslog</code>. Cron itself is solid, it&rsquo;s been quietly ticking along for decades, but its management interface is a text file you edit with <code>vi</code> and hope you didn&rsquo;t break the syntax.</p>
<p><a href="https://github.com/fccview/cronmaster" rel="nofollow">Cronmaster</a>
 is an open-source project, licensed under AGPL-3.0, that puts a modern web UI on top of the cron jobs running on a host (or a container), with human-readable syntax, live logging, and proper authentication. We evaluated it as an alternative to hand-editing crontab on servers where you actually want visibility into what&rsquo;s running and when.</p>
<h2 id="what-cronmaster-actually-is">What Cronmaster actually is</h2>
<p>Cronmaster is a Next.js + TypeScript app, styled with Tailwind CSS, shipped as a single Docker container. Instead of shelling out to <code>crontab</code> commands under the hood, it reads and writes the host&rsquo;s crontab file directly, which lets it stay in sync with the actual system state in real time, even while Cronmaster itself runs containerized next to the host&rsquo;s cron.</p>
<p>The UI shows your existing jobs in plain English (&ldquo;every day at 3:00 AM&rdquo; instead of <code>0 3 * * *</code>), lets you create, edit, clone, and delete jobs straight from the browser, and ships with presets for common schedules so you&rsquo;re not googling cron syntax for the tenth time.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The real problem with cron isn&rsquo;t that it doesn&rsquo;t work, it&rsquo;s that it&rsquo;s opaque. When a job fails silently at 4am, you find out only once someone notices the report is missing or the backup never ran. This is exactly what Cronmaster addresses: optional per-job logging (stdout, stderr, exit code, timestamp) with automatic cleanup, and live updates over Server-Sent Events, so you can watch a long-running job execute in real time from your browser instead of <code>tail -f</code>-ing some log file.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Modern, responsive UI</strong> with dark/light mode.</li>
<li><strong>System monitoring</strong>: uptime, memory, CPU, GPU, and network info for the host, right in the same dashboard.</li>
<li><strong>Cron job management</strong>: create, edit, delete, clone, with human-readable syntax and quick presets.</li>
<li><strong>Bash script management</strong>: create and manage scripts straight from the UI, for use inside jobs.</li>
<li><strong>Optional execution logging</strong>, with automatic history cleanup, capturing stdout/stderr/exit code/timestamps.</li>
<li><strong>Live updates via SSE</strong>: real-time status and log streaming for long-running jobs.</li>
<li><strong>Smart execution</strong>: jobs with logging enabled run asynchronously with background updates; jobs without logging run synchronously with a 5-minute timeout.</li>
<li><strong>Full REST API</strong>, with optional API key authentication, for external integrations.</li>
<li><strong>Flexible authentication</strong>: simple password, OIDC/SSO (Authentik, Auth0, Keycloak, Okta, Google, Entra ID), or both at once.</li>
<li><strong>Localization</strong> across multiple languages, with support for custom translations.</li>
<li><strong>ARM64 and AMD64 support</strong>, so it runs on a Raspberry Pi just as well as an x86 VPS.</li>
</ul>
<h2 id="installing-cronmaster-on-a-vps">Installing Cronmaster on a VPS</h2>
<p>Cronmaster runs exclusively as a container, and the official image is on GHCR. The minimal setup from the project&rsquo;s README:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat &gt; docker-compose.yml <span style="color:#e6db74">&lt;&lt; &#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">services:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  cronmaster:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    image: ghcr.io/fccview/cronmaster:latest
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    container_name: cronmaster
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    user: &#34;root&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    ports:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - &#34;40123:3000&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    environment:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - NODE_ENV=production
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - AUTH_PASSWORD=your_strong_password
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - HOST_CRONTAB_USER=root
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    volumes:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - /var/run/docker.sock:/var/run/docker.sock
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - ./scripts:/app/scripts
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - ./data:/app/data
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - ./snippets:/app/snippets
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    pid: &#34;host&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    privileged: true
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    restart: always
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    init: true
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>Notice it runs <code>privileged: true</code>, mounts the Docker socket, and shares <code>pid: host</code>, because it needs direct access to the host&rsquo;s crontab and, optionally, to other containers. That&rsquo;s a high level of trust handed to the container, treat it accordingly (isolate it on a dedicated VPS, not next to other apps holding sensitive data).</p>
<p>For the firewall, only expose the UI port (40123 here) behind a TLS-terminating reverse proxy, never directly public without authentication already configured:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>A modest <a href="/en/virtual-servers/vps/">VPS</a>
 is enough for Cronmaster itself, but size it based on whatever runs inside the jobs you&rsquo;re managing, not the management app.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="sso-instead-of-a-shared-password">SSO instead of a shared password</h3>
<p>If more than one person on the team needs access to the same cron jobs (say, DevOps plus on-call), configure OIDC instead of a shared password. Cronmaster supports any standard OIDC provider, so it plugs into whatever you already have (a self-hosted Keycloak, or Google/Entra ID if you&rsquo;re already on those). You can enable both methods at once, handy for a local break-glass account plus SSO for everyone else.</p>
<h3 id="selective-logging-not-blanket-logging">Selective logging, not blanket logging</h3>
<p>Don&rsquo;t flip logging on for every job by default. Jobs without logging run synchronously with a strict 5-minute timeout, which is fine for short, deterministic tasks. Jobs with logging run asynchronously, making them the right choice for anything that runs longer or produces output you want to inspect later. Mixing the two up can produce unexpected timeouts on jobs that actually needed background execution.</p>
<h3 id="api-key-for-external-integrations">API key for external integrations</h3>
<p>If you already have an alerting system or an internal dashboard, use the REST API with API key authentication instead of scraping the UI. Full documentation lives in <code>howto/API.md</code> in the repo, and the API lets you read job status and logs programmatically, exactly what you need to fold Cronmaster into a broader monitoring flow.</p>
<h3 id="reusable-script-snippets">Reusable script snippets</h3>
<p>The bash script management feature lets you keep reusable code snippets attached to jobs. Instead of copy-pasting the same shell block into ten different jobs, you keep it in one place and reference it, cutting the risk that a fix gets applied to nine out of ten copies.</p>
<h2 id="where-cronmaster-is-not-the-right-answer">Where Cronmaster is not the right answer</h2>
<p>If you have a single machine with two or three simple, stable cron jobs, spinning up a privileged container for that is overkill, just edit crontab directly and move on. Cronmaster also doesn&rsquo;t replace a real workflow orchestrator (Airflow, Temporal, or even a job scheduler with proper retry logic and task dependencies); it&rsquo;s still cron underneath, with the same limitations (no sophisticated automatic retries, no dependency DAGs). And given the <code>privileged: true</code> requirement plus Docker socket access, don&rsquo;t put it on a multi-tenant host or a machine with other sensitive apps without seriously thinking through isolation first.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://github.com/jhuckaby/Cronicle" rel="nofollow">Cronicle</a>
</strong>: a more complete scheduler with clustering and job dependencies, but a steeper learning curve and a heavier install than a single container.</li>
<li><strong>Plain crontab + <code>healthchecks.io</code></strong> (self-hosted as <a href="https://github.com/healthchecks/healthchecks" rel="nofollow">Healthchecks</a>
): keep cron simple and just bolt on monitoring via HTTP heartbeats, without changing how you manage jobs.</li>
<li><strong><a href="https://github.com/mcuadros/ofelia" rel="nofollow">Ofelia</a>
</strong>: a minimalist Docker scheduler defined via container labels, no UI, but very lightweight if you&rsquo;re already orchestrating everything with Docker Compose.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you manage more than a handful of cron jobs and have ever caught yourself manually checking whether last night&rsquo;s backup ran, Cronmaster is worth trying. The human-readable UI and live logging save real time, and the SSO support is welcome the moment you&rsquo;re not the only one who needs access. Just weigh the privilege requirements seriously before putting it on anything that matters.</p>
<p>Want a dedicated <a href="/en/virtual-servers/vps/">VPS</a>
 for your ops tooling, isolated from the rest of your infrastructure? Or would you rather hand off the setup and maintenance entirely? Our <a href="/en/services/server-administration/">server administration</a>
 service covers exactly that.</p>
]]></content:encoded><category>DevOps</category><category>Automation</category></item><item><title>ArchiveBox: self-hosted web archiving that actually beats link rot</title><link>https://dreamserver.ro/en/blog/archivebox-web-archiving/</link><pubDate>Wed, 15 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/archivebox-web-archiving/</guid><description>ArchiveBox saves every URL as HTML, PDF, screenshot, WARC and more, all at once. We run it in our Bucharest datacenter to stop link rot before it costs us a source.</description><content:encoded><![CDATA[<p>Ever saved a link that mattered, an article you cited, evidence for a case, a source referenced in a report, and gone back to find the page just gone? Domain expired, article deleted, site redesigned three times since. There&rsquo;s a name for this: link rot. A 2024 Pew study found that nearly a quarter of web pages referenced between 2013 and 2023 have already disappeared. A bookmark isn&rsquo;t an archiving plan, it&rsquo;s just a bet that the page will still be there tomorrow. Often it isn&rsquo;t.</p>
<p><a href="https://github.com/ArchiveBox/ArchiveBox" rel="nofollow">ArchiveBox</a>
 (site: <a href="https://archivebox.io" rel="nofollow">archivebox.io</a>
) is an open-source, MIT-licensed tool built for exactly that problem: you give it a URL, and it saves a complete copy, in multiple formats at once, on your own server. We run ArchiveBox in our Bucharest datacenter to stop link rot on our own reference material and internal documentation before it becomes a problem.</p>
<h2 id="what-archivebox-actually-is">What ArchiveBox actually is</h2>
<p>ArchiveBox is built on Django and Django-Ninja, with SQLite for persistent metadata storage. When you feed it a URL, it doesn&rsquo;t take a single snapshot, it orchestrates a set of proven tools to produce, simultaneously: a full HTML snapshot (original HTML+CSS+JS), a &ldquo;SingleFile&rdquo; page (the whole page bundled into one self-contained HTML file), a PDF, a PNG screenshot, a WARC file (the same standard format used by the Internet Archive), extracted article text, the favicon, HTTP headers, and downloaded media via <code>yt-dlp</code> when the page contains video or audio.</p>
<p>Every URL becomes a &ldquo;snapshot&rdquo; with its own folder, an HTML index, and all the formats above sitting next to each other. You&rsquo;re not betting on a single format staying usable (a PDF that renders wrong later, an HTML page that lost its external CSS), you always have redundant copies.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference from a plain <code>wget</code> or a manual screenshot is redundancy plus automation. If you&rsquo;re archiving sources for an article, evidence for a legal matter, or just your own research history, you want to be able to open the archive in five years regardless of what&rsquo;s changed in browsers or file formats by then. WARC is the same format used by the Internet Archive&rsquo;s Wayback Machine, so you get compatibility with established replay tooling. You&rsquo;re effectively building your own Wayback Machine, one you actually control.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Multiple ways to add URLs</strong>: CLI (<code>archivebox add &lt;url&gt;</code>), a REST API (beta), a browser bookmarklet, or bulk import from browser history, bookmarks, Pocket, Pinboard, Reddit, or RSS feeds.</li>
<li><strong>Full-text search</strong> across everything you&rsquo;ve archived, powered by Sonic (a fast, lightweight search engine).</li>
<li><strong>Web UI</strong> (Django Admin plus its own interface) for browsing, searching, and managing snapshots.</li>
<li><strong>Scheduled importing</strong>: configure RSS/JSON/CSV sources that get checked periodically, and any new URL gets archived automatically.</li>
<li><strong>An official browser extension</strong> (ArchiveBox Exporter, also available in the Chrome Web Store) for one-click archiving straight from the current tab.</li>
<li><strong>Optional redundant backup to archive.org</strong>, if you want a copy outside your own infrastructure too.</li>
<li>The client interface uses plain HTML/CSS/JS with no heavy frontend framework, so it runs comfortably even on a modest VPS.</li>
</ul>
<h2 id="installing-archivebox-on-a-vps">Installing ArchiveBox on a VPS</h2>
<p>The officially recommended method is Docker Compose. A minimal <code>docker-compose.yml</code> setup looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir archivebox <span style="color:#f92672">&amp;&amp;</span> cd archivebox
</span></span><span style="display:flex;"><span>curl -O https://raw.githubusercontent.com/ArchiveBox/ArchiveBox/dev/docker-compose.yml
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker compose run archivebox init --setup
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>This creates a <code>data/</code> folder with your archive and starts the web server on port 8000 by default. Create an admin account with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose run archivebox manage createsuperuser
</span></span></code></pre></div><p>If you&rsquo;d rather skip Docker, the package is also available via <code>uv</code>, <code>apt</code>, <code>brew</code>, or <code>pacman</code>, depending on your distro.</p>
<p>Once it&rsquo;s running, only expose it publicly behind a TLS-terminating reverse proxy (Nginx, Caddy, or Traefik), never the raw port. On the VPS firewall itself, allow only 22 (SSH), 80, and 443:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>Budget reasonable resources: headless Chrome/Chromium for rendering, plus generous disk space if you&rsquo;re archiving media (yt-dlp can pull down large files). A <a href="/en/virtual-servers/vps/">VPS</a>
 with at least 2 vCPUs and enough SSD for your archive&rsquo;s growth is a solid starting point.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="auto-importing-from-recurring-sources">Auto-importing from recurring sources</h3>
<p>Instead of adding every URL by hand, configure <code>archivebox schedule</code> or an external cron job that runs <code>archivebox add --index-only</code> against an RSS feed or a periodically refreshed bookmark export. That turns ArchiveBox from a reactive tool (you archive when you remember) into a proactive one (it archives everything you post or read automatically).</p>
<h3 id="picking-extractors-selectively">Picking extractors selectively</h3>
<p>You don&rsquo;t need all 10+ formats for every archive. If you&rsquo;re running on a small VPS and only want text plus PDF (skipping video, skipping heavy WARC files), disable specific extractors in <code>ArchiveBox.conf</code> (settings like <code>SAVE_WARC=False</code> or <code>SAVE_MEDIA=False</code>). This dramatically cuts disk usage and per-URL processing time.</p>
<h3 id="keeping-big-archives-off-the-root-disk">Keeping big archives off the root disk</h3>
<p>If your archive grows (media, full WARCs), you don&rsquo;t want to fill up the VM&rsquo;s root filesystem. Mount a separate disk at <code>data/archive/</code> before you start archiving large volumes, exactly like you would for any app whose data keeps growing.</p>
<h3 id="full-text-search-via-sonic">Full-text search via Sonic</h3>
<p>Sonic runs as a separate container next to ArchiveBox and indexes the extracted text content. If your archive grows into thousands of pages, full-text search is what makes the tool actually useful, otherwise you&rsquo;re browsing by title and date alone.</p>
<h2 id="where-archivebox-is-not-the-right-answer">Where ArchiveBox is not the right answer</h2>
<p>If all you need is &ldquo;save me 20 links once a month,&rdquo; ArchiveBox is overkill, a plain bookmark manager or even the public Wayback Machine (<code>web.archive.org/save</code>) gets it done without you having to run a server. It&rsquo;s also not a CMS or a publishing tool, archives are meant for your own reference, not for serving as a high-traffic public site (even though technically you could). If your goal is real-time content monitoring (detecting changes on a page, alerting), look for a dedicated change-detection tool instead of an archiver. And if you legally need notarized, timestamp-certified archives (admissible evidence with a strict chain of custody), talk to a provider specialized in legal archiving, raw WARC files don&rsquo;t give you that out of the box.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://github.com/gildas-lormeau/SingleFile" rel="nofollow">SingleFile</a>
</strong>: a browser extension that saves a page as a single HTML file. Great for one-off captures, but no orchestration, search, or central archive management.</li>
<li><strong><a href="https://web.archive.org/" rel="nofollow">Wayback Machine (archive.org)</a>
</strong>: the Internet Archive&rsquo;s public service. Free and simple, but you don&rsquo;t control retention, and some pages block their crawler (robots.txt, geo-blocking).</li>
<li><strong><a href="https://github.com/Y2Z/monolith" rel="nofollow">Monolith</a>
</strong>: a minimalist Rust CLI that bundles a page into a single HTML file. Fast and lightweight, but no UI, search, or multi-format output.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you produce or rely on content that matters long-term, cited articles, research material, evidence, external documentation you link to, ArchiveBox is a cheap tool (resource-wise) that solves a real and often-ignored problem. We run it for exactly that in our Bucharest datacenter, and we haven&rsquo;t been caught out by a dead link to something we actually needed to reread since.</p>
<p>Need a <a href="/en/virtual-servers/vps/">VPS</a>
 to put it to work, or would you rather not handle the day-to-day operations yourself? Our <a href="/en/services/server-administration/">server administration</a>
 service covers exactly that, from install through updates and monitoring.</p>
]]></content:encoded><category>Archiving</category><category>Apps</category></item><item><title>WatchYourLAN: knowing what's actually connected to your network, right now</title><link>https://dreamserver.ro/en/blog/watchyourlan-network-host-discovery/</link><pubDate>Sun, 12 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/watchyourlan-network-host-discovery/</guid><description>WatchYourLAN scans your network via ARP and tracks every host it finds. We run it ourselves in our Bucharest datacenter, and here's how to install it.</description><content:encoded><![CDATA[<p>How many devices are actually connected to your network right now? Not how many you think there are, not how many show up in the diagram someone drew two years ago when the office got cabled, but how many actually answer an ARP scan this second. If you can&rsquo;t answer that in under a minute, you have a visibility problem. A test laptop forgotten and left plugged in for six months, a switch someone &ldquo;temporarily&rdquo; added to get more ports, a cheap IoT gadget a colleague installed without telling anyone, all of that lives comfortably on your network until someone actually goes looking. The problem isn&rsquo;t a lack of firewall rules or policy. The problem is you can&rsquo;t defend what you don&rsquo;t know exists.</p>
<p>That&rsquo;s exactly what WatchYourLAN (github.com/aceberg/WatchYourLAN) is built for. It&rsquo;s an open source project written in Go: it ARP-scans your subnet, remembers every host it finds, with MAC address, hostname, interface, and online/offline history, and puts all of it in a simple web UI, plus a <code>/metrics</code> endpoint for Prometheus. We run it ourselves, across our own network in our Bucharest datacenter: we run one instance per internal segment where we want direct visibility, and the results feed into our Grafana stack through that <code>/metrics</code> endpoint. We&rsquo;re not selling it as a product, it&rsquo;s an internal operational tool we&rsquo;ve found useful enough to write about.</p>
<h2 id="what-watchyourlan-actually-is">What WatchYourLAN actually is</h2>
<p>WatchYourLAN doesn&rsquo;t do anything exotic, and that&rsquo;s precisely its strength. It runs periodic ARP scans across one or more configured interfaces/subnets, and for every MAC address it finds, it records: the current IP, hostname (if it can be resolved), the interface it was seen on, online/offline status, and first-seen/last-seen timestamps. All of that gets written to a local SQLite database, so there&rsquo;s no separate database server or heavy external dependency to run. Everything you see in the web UI, a list of hosts with IP, MAC, name, interface, and a known/unknown flag, comes straight out of that history.</p>
<p>On top of the UI, it exposes a <code>/metrics</code> endpoint in Prometheus format, which means you can scrape that data into whatever monitoring stack you already have, without writing the integration yourself from scratch. It&rsquo;s written in Go, runs comfortably in a container, and is designed to work with host networking, because otherwise it has no way to actually see the real ARP traffic on the physical interface or VLAN you want to monitor.</p>
<h3 id="why-it-matters">Why it matters</h3>
<p>A network inventory &ldquo;on paper&rdquo; (or in a spreadsheet last updated six months ago) lies to you constantly. Real network state changes daily: someone plugs in an unmanaged switch to add a few more ports, a test box stays powered on after the project ends, an IoT device shows up out of nowhere on the wrong VLAN. None of these show up in a quarterly audit, but all of them show up immediately in an ARP scan. WatchYourLAN answers &ldquo;what&rsquo;s actually alive on this segment right now, and since when,&rdquo; which is exactly the kind of signal you want when you&rsquo;re trying to spot unwanted equipment or simply confirm your network segmentation looks the way you think it does.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li>A web UI listing every discovered host: IP address, MAC address, hostname/name, network interface it was seen on, and a known/unknown flag.</li>
<li>Online/offline history per host, with first-seen and last-seen timestamps, so you can answer &ldquo;since when has this thing been connected&rdquo; without digging through logs.</li>
<li>A Prometheus-compatible <code>/metrics</code> endpoint, ready to scrape for dashboards and alerts in Grafana or anything else that speaks the Prometheus format.</li>
<li>Local storage in SQLite, no heavy external dependencies, easy to back up or reset.</li>
<li>A lightweight Go binary that runs well in a container, typically with host networking so it can actually see real network traffic.</li>
</ul>
<h2 id="installing-watchyourlan-on-a-vps">Installing WatchYourLAN on a VPS</h2>
<p>The simplest way to try WatchYourLAN is in a container, with access to the interface you want to scan. A minimal <code>docker run</code> example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name watchyourlan <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --network host <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e TZ<span style="color:#f92672">=</span>Europe/Bucharest <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e IFACES<span style="color:#f92672">=</span>eth0 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v /docker/watchyourlan/data:/data <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  aceberg/watchyourlan:latest
</span></span></code></pre></div><p>Or, if you prefer docker compose:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">watchyourlan</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">aceberg/watchyourlan:latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">watchyourlan</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">network_mode</span>: <span style="color:#ae81ff">host</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">TZ=Europe/Bucharest</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">IFACES=eth0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./data:/data</span>
</span></span></code></pre></div><p>The data volume holds the SQLite database, so keep it persistently mounted if you don&rsquo;t want to lose host history on a container restart. The web UI runs on its own port, and if you expose that port outside the box, mind your <code>ufw</code> rules: don&rsquo;t leave it open publicly without restriction. Either bind it to an internal address or a VPN only, or put it behind a reverse proxy with authentication, because a list of hosts, MAC addresses, and interfaces is effectively a map of your network topology, not something you want exposed to whoever happens to pass by.</p>
<p>One thing worth saying plainly: on a typical VPS, with a single network interface in a public cloud, WatchYourLAN has a narrower use case. ARP scanning only works within the same broadcast domain, and a standard VPS usually sits isolated from other tenants on the provider&rsquo;s network, so it has nothing to &ldquo;see&rdquo; belonging to neighboring machines. Where it genuinely shines is on a segmented network, with VLANs or subnets you control, whether that&rsquo;s internal infrastructure or a private network/VPC environment where host networking actually gives you access to real traffic. If you have a VPS with us and want to try the tool for a narrower use case (say, monitoring a private network reachable over a VPN into that VPS), you can start from our <a href="/en/virtual-servers/vps/">VPS</a>
 page; for more complex topologies with multiple VLANs, our <a href="/en/network/">network</a>
 page has more context.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="scan-interval">Scan interval</h3>
<p>WatchYourLAN scans periodically, not continuously, and the interval between scans is a trade-off between &ldquo;I see changes near real time&rdquo; and &ldquo;I&rsquo;m not generating unnecessary ARP traffic on the network.&rdquo; For a small or critical VLAN, where you want to catch a newly connected device quickly, a short interval makes sense. For a large segment with hundreds of hosts, a more relaxed interval avoids turning the scan itself into constant background noise. There&rsquo;s no universally correct value, it depends on how often your network actually changes and how fast you want to know.</p>
<h3 id="known-versus-unknown">Known versus unknown</h3>
<p>The known/unknown host flag is, in our opinion, the most practical feature of the whole UI. Once you&rsquo;ve marked the hosts you expect (servers, workstations, network gear), any new appearance jumps out immediately as &ldquo;unknown,&rdquo; instead of getting lost in a long list of IPs. It&rsquo;s worth making a habit of periodically going through the list and marking what&rsquo;s legitimate, otherwise the &ldquo;unknown&rdquo; list itself becomes noise you learn to ignore.</p>
<h3 id="wiring-metrics-into-prometheus-and-grafana">Wiring /metrics into Prometheus and Grafana</h3>
<p>Because WatchYourLAN exposes <code>/metrics</code> in Prometheus format, integrating it into an existing stack is straightforward: add a scrape job pointing at that endpoint, and from there you can build Grafana panels for online host counts per VLAN, new hosts that appeared in the last 24 hours, or alerts when a known, important host suddenly disappears from the network. That&rsquo;s exactly what we do: our scan results land in Grafana alongside the rest of our infrastructure metrics, so we get one view, not a separate tab we forget is open.</p>
<h3 id="one-instance-per-vlansegment">One instance per VLAN/segment</h3>
<p>If your network is segmented (and it probably should be), the cleanest model is running one WatchYourLAN instance per VLAN or subnet that matters, each with visibility limited to that interface/segment. That gives you both isolation (one broken or compromised instance doesn&rsquo;t take down your visibility elsewhere) and clarity in the dashboard: you know exactly which VLAN is reporting what. That&rsquo;s the approach we use internally too, one instance per segment that needs direct visibility.</p>
<h2 id="where-watchyourlan-is-not-the-right-answer">Where WatchYourLAN is not the right answer</h2>
<p>Let&rsquo;s be honest: WatchYourLAN is not an IPAM. It doesn&rsquo;t manage your IP allocations, doesn&rsquo;t track reserved subnets or address planning, it just tells you what&rsquo;s alive right now. For an authoritative source of truth on topology, allocations, and infrastructure documentation, a dedicated CMDB/IPAM tool (we use NetBox internally for that) is still necessary.</p>
<p>It&rsquo;s also not a complete NAC (Network Access Control) or 802.1x solution. It tells you what it sees, but it doesn&rsquo;t decide who&rsquo;s allowed to connect and doesn&rsquo;t block anything at the switch port level. If you need identity-based network access control, you need something alongside it, not instead of it.</p>
<p>It&rsquo;s not a vulnerability scanner either. It tells you a host exists at an address, not what services are running on it or whether it has exploitable ports open. For that you need a dedicated, separate scanner.</p>
<p>And perhaps the most important technical detail: ARP scanning only works within the same broadcast domain. It can&rsquo;t &ldquo;see&rdquo; past a router, so on a routed network with multiple separate subnets, you need an instance (or visibility) on each segment individually, not one magic instance covering everything.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>Before settling on WatchYourLAN for our use case, we looked at a few other options, some complementary, some direct competitors:</p>
<ul>
<li><strong>nmap</strong> with custom scripts and a database behind it (often Postgres), for teams who want full control over scan format and what gets stored. Flexible, but you build all the persistence and UI yourself.</li>
<li><strong>arp-scan</strong>, the command-line tool that many other discovery tools conceptually build on. Great for one-off scans or custom scripting, but no persistent history or UI out of the box.</li>
<li><strong>Netdisco</strong>, an older project more focused on switch-level/topology discovery (including SNMP), useful when you want more than a plain ARP scan and also need switch port mapping.</li>
<li><strong>Fing</strong>, best known in its small/home network variant, focused on ease of use and mobile apps.</li>
<li><strong>Network discovery modules in Zabbix or PRTG</strong>, if you already run one of these monitoring systems in production and want discovery as an add-on feature rather than a separate tool to administer.</li>
</ul>
<p>Each of these has its place. We picked WatchYourLAN for how easy it is to deploy per segment, and because <code>/metrics</code> plugs directly into our existing Grafana stack without adding another separate piece of software to manage.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you run a segmented network and want to be able to answer &ldquo;what&rsquo;s connected here right now, and since when&rdquo; at any moment, WatchYourLAN is a lightweight, free tool that&rsquo;s honest about what it does: an ARP scanner with memory and a metrics endpoint, nothing more, nothing less. You can try it on one of our <a href="/en/virtual-servers/vps/">VPS</a>
 instances if you want to play with it in an isolated environment, and if you want it properly wired into a segmented network with VLANs, with a discovery and monitoring workflow set up from scratch, our <a href="/en/services/server-administration/">server administration</a>
 team can handle the configuration and integration, so you&rsquo;re not left with yet another tab you only check once a month.</p>
]]></content:encoded><category>Network</category></item><item><title>OpenMediaVault: a Debian-based NAS you run entirely from a browser</title><link>https://dreamserver.ro/en/blog/openmediavault-nas-server/</link><pubDate>Wed, 08 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/openmediavault-nas-server/</guid><description>OpenMediaVault is a Debian-based NAS solution with SMB/NFS/FTP, software RAID and a plugin system. What it offers and why it wants dedicated disks, not a VPS.</description><content:encoded><![CDATA[<p>You&rsquo;ve got a few drives, an old server or a mini-PC, and you want a central place to keep backups, photos and movies, reachable from the network on any device. Two classic paths: buy a commercial NAS box (Synology, QNAP), pay for a closed firmware and hope security updates keep coming, or hand-configure Samba, NFS and <code>mdadm</code> on plain Linux, which works, except every new share or added disk means editing config files and restarting services.</p>
<p><a href="https://github.com/openmediavault/openmediavault" rel="nofollow">OpenMediaVault</a>
 (<a href="https://www.openmediavault.org" rel="nofollow">openmediavault.org</a>
) sits in between: a complete NAS distribution built on Debian but managed entirely from a web interface, licensed under GPLv3. We evaluated it as an option for anyone who wants the convenience of a commercial box but on their own hardware, with full control over the system underneath.</p>
<h2 id="what-openmediavault-actually-is">What OpenMediaVault actually is</h2>
<p>OpenMediaVault (often shortened to OMV) is, at its core, a stable Debian install with a management layer on top: a daemon and a web interface (built on Angular/PHP) that translates clicks in a friendly UI into system commands - creating software RAID, sharing over SMB, configuring users and permissions, monitoring disks. It&rsquo;s the spiritual successor to the old FreeNAS (before it became TrueNAS), but built on Debian/Linux instead of FreeBSD, which means broader hardware and driver compatibility.</p>
<p>Redundant storage relies on two complementary mechanisms: <strong>software RAID via mdadm</strong> (classic RAID1/5/6/10 at the Linux kernel level) for real-time redundancy, and <strong>SnapRAID</strong>, a more unconventional tool that computes parity information periodically (not in real time), suited for archives of data that don&rsquo;t change often - photos, movies, backups - where you don&rsquo;t need instant protection against a dead disk, but you do want to be able to rebuild the data if it happens.</p>
<p>What makes OMV genuinely useful long-term is its plugin system, <code>omv-extras</code>: an additional package repository that extends the base install with ZFS support, Docker/Portainer integration for running containers directly on the storage box, cloud backup clients and more, all without leaving the web UI.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference from a commercial NAS box isn&rsquo;t only hardware cost. With a closed firmware, you&rsquo;re at the vendor&rsquo;s mercy for how long you keep getting security updates and what features get added or dropped in a new release. With OMV, you have a standard Debian underneath: you can install any package from the Debian repositories, upgrade the operating system independently of OMV, and if the OMV project itself disappeared tomorrow, you&rsquo;d still have a working Linux server with Samba and mdadm configured, not a sealed box turning into e-waste.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li>A full web interface for managing disks, volumes and shares, no command line needed for day-to-day operations</li>
<li>File services: SMB/CIFS (Windows/macOS/Linux), NFS (Linux/Unix), FTP, SSH, rsync</li>
<li>Software RAID via mdadm (RAID0/1/5/6/10) plus SnapRAID as an alternative for rarely-modified data</li>
<li>User and group system with per-share permissions, plus support for external authentication (LDAP/AD)</li>
<li>SMART monitoring for disks, email notifications on events and thresholds</li>
<li>A plugin system (<code>omv-extras</code>) for ZFS, Docker/Portainer, cloud clients and more</li>
<li>Support for x86_64 and ARM architectures (including Raspberry Pi), useful for low-power boxes</li>
</ul>
<h2 id="installing-openmediavault-on-a-vps">Installing OpenMediaVault on a VPS</h2>
<p>Let&rsquo;s be upfront: OpenMediaVault is designed for physical hardware with dedicated disks, not a regular VPS with a single virtual disk. That said, it can make sense to test your configuration or run a management/monitoring instance on a <a href="/en/virtual-servers/vps/">VPS</a>
 before deploying to physical hardware, or to validate a new plugin without touching the production box.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># install on a clean Debian machine (classic path, on top of an existing OS)</span>
</span></span><span style="display:flex;"><span>wget -O - https://packages.openmediavault.org/public/archive.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/openmediavault-archive-keyring.gpg
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;deb [signed-by=/etc/apt/trusted.gpg.d/openmediavault-archive-keyring.gpg] https://packages.openmediavault.org/public sandworm main&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | tee /etc/apt/sources.list.d/openmediavault.list
</span></span><span style="display:flex;"><span>apt update
</span></span><span style="display:flex;"><span>apt install openmediavault-keyring
</span></span><span style="display:flex;"><span>apt update
</span></span><span style="display:flex;"><span>apt install openmediavault
</span></span></code></pre></div><p>After install, the web UI listens on port 80/443 by default, and the initial login uses the <code>admin</code> user. In a VPS test environment with no extra physical disks attached, you can still practice configuring SMB/NFS shares on the main volume, though you won&rsquo;t be able to demonstrate RAID or SnapRAID without separate attached disks.</p>
<pre tabindex="0"><code class="language-ufw" data-lang="ufw"># web admin interface
ufw allow 80/tcp
ufw allow 443/tcp
# SMB/CIFS
ufw allow 445/tcp
ufw allow 139/tcp
# NFS (portmapper + nfsd; exact port differs between NFSv3 and NFSv4)
ufw allow 2049/tcp
ufw allow 111/tcp
# FTP, if you enable it
ufw allow 21/tcp
</code></pre><p>On a VPS, consider restricting these rules to a single IP or a private network rather than exposing SMB/NFS publicly - these are protocols designed for trusted local networks, not the open internet.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="choose-raid-or-snapraid-based-on-the-data-type">Choose RAID or SnapRAID based on the data type</h3>
<p>For data that changes frequently and needs immediate availability after a disk failure, mdadm (RAID1/5/6) is the right call. For relatively static archives - media libraries, periodic backups - SnapRAID gives you the flexibility to add disks of different sizes at any point, something classic RAID doesn&rsquo;t allow easily.</p>
<h3 id="dont-skip-smart-monitoring">Don&rsquo;t skip SMART monitoring</h3>
<p>OMV has native integration with <code>smartmontools</code>, but email notifications need to be configured explicitly (SMTP server, temperature threshold, reallocated-sector threshold). A disk that starts throwing SMART errors weeks before it fully dies is exactly the scenario where you want to be alerted automatically, not to find out after you&rsquo;ve already lost data.</p>
<h3 id="plugins-mean-an-extra-repository-not-magic">Plugins mean an extra repository, not magic</h3>
<p><code>omv-extras</code> adds a package repository on top of standard Debian. Before enabling experimental plugins (especially community ones, not official ones), check which OMV version they&rsquo;re actively maintained against - the project evolves, and abandoned plugins can end up incompatible after a major upgrade.</p>
<h3 id="docker-through-omv-vs-docker-managed-manually">Docker through OMV vs. Docker managed manually</h3>
<p>The Docker/Portainer plugin is convenient for running a handful of extra containers (say, a cloud sync service) directly on the NAS box, but if your plan is to run a complex, actively maintained Docker stack, consider separating it onto a dedicated machine - a NAS loaded with too many roles becomes harder to troubleshoot when something breaks.</p>
<h2 id="where-openmediavault-is-not-the-right-answer">Where OpenMediaVault is not the right answer</h2>
<p>If you need storage for a production environment with serious availability requirements (automatic failover, multi-node replication), OMV isn&rsquo;t built for that - it&rsquo;s a single-node NAS solution. For that, look at Ceph or a dedicated cluster storage solution instead.</p>
<p>Also, on a regular <a href="/en/virtual-servers/vps/">VPS</a>
 with a single virtual disk already redundant at the hypervisor level, most of OMV&rsquo;s value (software RAID, SnapRAID, SMART monitoring on physical disks) simply doesn&rsquo;t apply. OMV earns its place on physical hardware, with multiple disks mounted directly.</p>
<p>Finally, if all you need is a simple SMB share and nothing more, a standalone <code>samba</code> install without the OMV management layer stays easier to reason about and troubleshoot for anyone already comfortable with plain Linux.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://www.truenas.com" rel="nofollow">TrueNAS CORE/SCALE</a>
</strong>: built on native ZFS (CORE on FreeBSD, SCALE on Debian/Linux), with more advanced enterprise storage features, but higher hardware requirements (especially RAM for ZFS)</li>
<li><strong><a href="https://unraid.net" rel="nofollow">Unraid</a>
</strong>: a commercial (not free) solution very popular in the home-lab space, with a flexible parity model similar to SnapRAID, but a paid license</li>
<li><strong>Samba + mdadm managed manually</strong>: for anyone who wants full control with no management layer on top, at the cost of configuring everything from the command line</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>OpenMediaVault makes clear sense when you have dedicated physical hardware - a mini-PC, a repurposed old server, an ARM board with a few USB or SATA disks attached - and you want a friendly interface over Debian without giving up the flexibility of a standard Linux underneath. It&rsquo;s not, however, the right tool for storage on a regular VPS, where the virtual disk is already managed by the hypervisor.</p>
<p>If your plan is to pair a local OMV NAS with offsite backups or with services running in the cloud alongside it, a <a href="/en/virtual-servers/vps/">VPS</a>
 is the right place for the networking and externally-reachable services side of things, and if you&rsquo;d rather have someone handle maintenance and monitoring for the whole setup, check out our <a href="/en/services/server-administration/">server administration</a>
 service and our <a href="/en/network/">network section</a>
 for context on how all of this fits together in a full infrastructure.</p>
]]></content:encoded><category>Storage</category><category>NAS</category></item><item><title>Pterodactyl: the game server panel that saves you from SSH-ing in every time someone wants to restart Minecraft</title><link>https://dreamserver.ro/en/blog/pterodactyl-game-server-panel/</link><pubDate>Sun, 05 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/pterodactyl-game-server-panel/</guid><description>How Pterodactyl's Panel + Wings architecture gives game communities and hosting providers self-service control over Docker-isolated game servers.</description><content:encoded><![CDATA[<p>You know the pattern. Someone in the Discord wants a mod added to the Minecraft server. Someone else wants more RAM because the modpack got heavier. A friend wants the Rust server restarted because it froze at 3am. Every one of these routine requests turns into &ldquo;hey, can you SSH in and do X,&rdquo; and eventually you&rsquo;re the unpaid systems administrator for six people&rsquo;s hobby.</p>
<p>That&rsquo;s the exact problem Pterodactyl was built to solve. It&rsquo;s a free, open source game server management panel, <a href="https://github.com/pterodactyl/panel" rel="nofollow">MIT licensed</a>
, with the code on GitHub and documentation at <a href="https://pterodactyl.io" rel="nofollow">pterodactyl.io</a>
. We run it ourselves: DreamServer&rsquo;s dedicated game server hosting is built on Pterodactyl, precisely so customers get a real self-service panel instead of a support ticket queue for routine tasks like restarting a server or swapping a mod pack.</p>
<h2 id="what-pterodactyl-actually-is">What Pterodactyl actually is</h2>
<p>Pterodactyl splits into two pieces, and understanding that split is the key to understanding everything else about it.</p>
<p>The <strong>Panel</strong> is the web application, built on PHP and Laravel. It&rsquo;s what you and your users see: the dashboard, user accounts, the server list, billing hooks if you wire it up to something like WHMCS. The Panel doesn&rsquo;t run any game servers itself, it&rsquo;s the control plane: it knows what servers exist, who owns them, what resources they&rsquo;re allotted, and it talks to the machines that do the actual work.</p>
<p>Those machines run <strong>Wings</strong>, a daemon written in Go that lives on each game server node. Wings starts, stops, and manages the game server processes, launching each one inside its own Docker container. That&rsquo;s the detail that matters most operationally: every game server is isolated. A crashed or misbehaving Rust server can&rsquo;t take down the Minecraft server next to it on the same box, because they don&rsquo;t share a process space, and resource limits (CPU, RAM, disk) are enforced per container rather than being a polite suggestion.</p>
<p>The third piece is <strong>eggs</strong>: a templating system describing how to install, configure, and run a specific type of game server. An egg for Paper Minecraft knows how to download the right jar, generate the startup command, and expose the right config variables in the Panel UI. Official and community eggs exist for hundreds of games: Minecraft in all its flavors (Vanilla, Paper, Forge, Fabric), Rust, ARK: Survival Evolved, Valheim, Terraria, CS2 and other Source-engine titles, and plenty more. Adding support for a new game doesn&rsquo;t mean patching Pterodactyl itself, it means writing or importing an egg.</p>
<h3 id="why-it-matters">Why it matters</h3>
<p>The obvious win is self-service. A restart, a mod swap, a look at the console to see why the server crashed: none of that needs an admin with SSH access anymore. That&rsquo;s exactly the model behind our <a href="/en/dedicated-servers/gameservers/">dedicated game server hosting</a>
: the servers are Pterodactyl-managed, so a customer running a Minecraft or Rust server gets full self-service control without needing shell access to the host.</p>
<p>The less obvious win is safety. Docker isolation means a container that eats all its allocated RAM gets killed by Docker, not by taking the whole host down with it. Per-server resource limits mean one noisy neighbor can&rsquo;t starve everyone else&rsquo;s server on the same node, whether you&rsquo;re running this for six friends or six hundred paying customers.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Eggs for hundreds of games</strong>, covering the obvious ones (Minecraft variants, Rust, ARK, Valheim, Terraria, CS2) and plenty of community-maintained niche titles.</li>
<li><strong>Per-server resource limits</strong>: CPU, RAM, and disk quotas enforced through Docker, so allocations are actual guarantees, not gentleman&rsquo;s agreements.</li>
<li><strong>SFTP access per server</strong>, so users can manage worlds, configs, and mods directly without needing shell access to the host.</li>
<li><strong>A live console</strong> in the browser, streaming stdin/stdout, so you can watch a server boot or debug a crash without a terminal.</li>
<li><strong>Subusers</strong>, letting you hand a friend or a community moderator limited permissions on one specific server, without giving them the keys to your whole panel.</li>
<li><strong>Scheduled tasks and backups</strong>, so restarts, world backups, or command execution can run on a timer instead of needing a human to remember.</li>
<li><strong>A REST API</strong>, for anyone who wants to script server management, tie it into a Discord bot, or integrate it with a billing system.</li>
<li><strong>Multi-node scaling</strong>: one Panel can manage many Wings nodes, which can be spread across different physical machines or even different locations.</li>
</ul>
<h2 id="installing-pterodactyl-on-a-vps">Installing Pterodactyl on a VPS</h2>
<p>A realistic install has two moving parts that can live on the same box (fine for a small setup) or on separate machines (what you want once you&rsquo;re running more than a handful of servers).</p>
<p>The Panel wants a fairly standard web stack: PHP, MariaDB or MySQL, Redis, and a web server in front (Nginx or Caddy). The official install path is a web-based installer after you pull the code and get Composer dependencies in place. Community-maintained Docker Compose setups also exist if you&rsquo;d rather run the whole Panel stack as containers.</p>
<p>A typical bare-metal-style Panel setup looks roughly like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># System dependencies (Debian/Ubuntu example)</span>
</span></span><span style="display:flex;"><span>apt update <span style="color:#f92672">&amp;&amp;</span> apt install -y php8.3 php8.3-<span style="color:#f92672">{</span>cli,gd,mysql,mbstring,bcmath,xml,curl,zip,intl<span style="color:#f92672">}</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  mariadb-server nginx redis-server curl tar unzip git composer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Pull the panel and install PHP dependencies</span>
</span></span><span style="display:flex;"><span>mkdir -p /var/www/pterodactyl <span style="color:#f92672">&amp;&amp;</span> cd /var/www/pterodactyl
</span></span><span style="display:flex;"><span>curl -Lo panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz
</span></span><span style="display:flex;"><span>tar -xzvf panel.tar.gz <span style="color:#f92672">&amp;&amp;</span> chmod -R <span style="color:#ae81ff">755</span> storage/* bootstrap/cache/
</span></span><span style="display:flex;"><span>composer install --no-dev --optimize-autoloader
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Environment + database migration (interactive)</span>
</span></span><span style="display:flex;"><span>cp .env.example .env
</span></span><span style="display:flex;"><span>php artisan key:generate --force
</span></span><span style="display:flex;"><span>php artisan p:environment:setup
</span></span><span style="display:flex;"><span>php artisan p:environment:database
</span></span><span style="display:flex;"><span>php artisan migrate --seed --force
</span></span><span style="display:flex;"><span>php artisan p:user:make
</span></span></code></pre></div><p>Wings, the daemon that actually runs game servers, is a separate install and itself relies on Docker to launch containers. The modern, standard path is to run Wings as a systemd-managed service (or as a container) on each node you want to host servers on:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># On each Wings node (needs Docker installed first)</span>
</span></span><span style="display:flex;"><span>mkdir -p /etc/pterodactyl
</span></span><span style="display:flex;"><span>curl -L -o /usr/local/bin/wings <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  <span style="color:#e6db74">&#34;https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_amd64&#34;</span>
</span></span><span style="display:flex;"><span>chmod u+x /usr/local/bin/wings
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Grab a node token from the Panel&#39;s admin UI, then:</span>
</span></span><span style="display:flex;"><span>wings configure --panel-url https://panel.example.com <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --token &lt;node-token&gt; --node &lt;node-id&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemctl enable --now wings
</span></span></code></pre></div><p>For a small setup, Panel and Wings on the same VPS or dedicated server is perfectly fine. Once you&rsquo;re running enough concurrent players or servers that CPU contention becomes a real concern, splitting Wings onto its own dedicated box (or several, one per region) is the natural next step, and the Panel handles multiple nodes without any architectural change.</p>
<p>Ports to open depend on what&rsquo;s running where. The Panel needs the standard web ports, Wings exposes its own API port, and each game server needs whatever port range you allocate for it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 80/tcp        <span style="color:#75715e"># Panel HTTP (redirect to HTTPS)</span>
</span></span><span style="display:flex;"><span>ufw allow 443/tcp       <span style="color:#75715e"># Panel HTTPS</span>
</span></span><span style="display:flex;"><span>ufw allow 8080/tcp      <span style="color:#75715e"># Wings default API/SFTP port (adjust to your config)</span>
</span></span><span style="display:flex;"><span>ufw allow 25565:25665/tcp   <span style="color:#75715e"># example game server port range (Minecraft-style)</span>
</span></span><span style="display:flex;"><span>ufw allow 25565:25665/udp   <span style="color:#75715e"># if your games need UDP too</span>
</span></span></code></pre></div><p>This is exactly the kind of workload that fits well on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
 or a dedicated box: predictable CPU and I/O, Docker support out of the box, and enough headroom to run Panel, Wings, and a handful of game servers side by side without stepping on each other.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="split-panel-and-wings-across-nodes-early">Split Panel and Wings across nodes early</h3>
<p>If you expect this to grow past a hobby project, don&rsquo;t wait until you&rsquo;re CPU-starved to separate Panel and Wings. Put the Panel on a small, low-resource box (it&rsquo;s mostly PHP and a database), and size Wings nodes for the actual game workloads. Different regions can run their own Wings nodes pointed at the same Panel, which is how you&rsquo;d offer, say, an EU and a US location from one admin interface.</p>
<h3 id="pick-eggs-deliberately">Pick eggs deliberately</h3>
<p>Egg quality varies. Official eggs tend to be solid and kept up to date; community eggs range from excellent to abandoned. Before committing a game to production, read the egg&rsquo;s install script and startup command. For something unusual (a specific mod loader version, custom plugins baked into the image), you may end up forking or writing your own egg rather than trusting a random one you found.</p>
<h3 id="set-resource-limits-with-headroom">Set resource limits with headroom</h3>
<p>A container that hits its memory limit gets killed by Docker&rsquo;s OOM handling, which for a game server usually means an ungraceful crash, not a clean shutdown. Size RAM limits with a margin above what the process normally needs (Minecraft with a big modpack is the classic case where people set <code>-Xmx</code> right at the container limit and wonder why it dies under load). CPU limits are softer: Docker throttles rather than kills, so a tight CPU limit just means lag.</p>
<h3 id="use-subusers-and-put-backups-off-node">Use subusers, and put backups off-node</h3>
<p>Give trusted community admins subuser access to their specific server rather than sharing the owner&rsquo;s login: permissions are granular enough for console access and file management without handing over billing or delete rights. And decide deliberately where backups live: local backups protect against a corrupted world file, not a dead disk. Pterodactyl supports S3-compatible storage as a backup destination, the sane default for anything a community actually depends on.</p>
<h3 id="lock-the-panel-down-like-the-admin-interface-it-is">Lock the Panel down like the admin interface it is</h3>
<p>The Panel has full control over every server it manages, so treat access accordingly: two-factor authentication for admin accounts, a small number of full-admin users, and HTTPS with a real certificate, not a self-signed one users have to click through.</p>
<h2 id="where-pterodactyl-is-not-the-right-answer">Where Pterodactyl is not the right answer</h2>
<p>If you&rsquo;re running exactly one Minecraft server for yourself, with nobody else needing access and no need for a web UI, Pterodactyl is overkill. A game binary in a systemd unit or a tmux/screen session, with a couple of shell aliases for start/stop/backup, has essentially zero overhead and nothing to maintain beyond the game itself. Panel plus Wings plus MariaDB plus Redis is real infrastructure that needs updates, monitoring, and patching. That&rsquo;s a fair trade when you&rsquo;re managing servers for other people, and a bad trade when you just want your own Valheim world up on weekends.</p>
<p>It&rsquo;s also not a universal fit for every game server binary out there. Anything that doesn&rsquo;t containerize cleanly, or expects direct hardware access, can be awkward to wrap in an egg. And egg quality is uneven: popular games are well covered, obscure ones may have a thin or unmaintained egg, or none at all, meaning you&rsquo;re writing your own.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><strong><a href="https://craftycontrol.com/" rel="nofollow">Crafty Controller</a>
</strong>: simpler and more narrowly focused on Minecraft. If Minecraft is genuinely the only game you&rsquo;ll ever run, Crafty&rsquo;s lower complexity is a real advantage over Pterodactyl&rsquo;s broader, heavier stack.</p>
<p><strong><a href="https://cubecoders.com/AMP" rel="nofollow">AMP (Application Management Panel)</a>
</strong>: commercial-friendly, broad game support, and a polished out-of-box experience from CubeCoders. Not fully open source the way Pterodactyl is, and licensing costs scale with usage, but a legitimate option if you want a supported commercial product.</p>
<p><strong><a href="https://github.com/pelican-dev/panel" rel="nofollow">Pelican Panel</a>
</strong>: a newer, Pterodactyl-inspired fork with its own development direction. Worth watching, though less battle-tested simply by virtue of being younger.</p>
<p><strong>Plain systemd or tmux/screen</strong>: for the single-server, single-admin case, this is genuinely the right answer. No web stack, no Docker layer, just the game process and your shell.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;re managing more than one or two game servers, or if anyone besides you needs to touch them, Pterodactyl earns its keep. The Panel plus Wings architecture, Docker isolation, and the egg system cover a genuinely wide range of games with real safety guarantees around resource limits, and the self-service model means you stop being the bottleneck for routine requests.</p>
<p>If you&rsquo;d rather skip building that stack yourself, our <a href="/en/dedicated-servers/gameservers/">dedicated game server hosting</a>
 runs on Pterodactyl already: you get the Panel, console, SFTP, and subuser access without standing up the infrastructure. If you want to run your own install, a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
 is a solid place to put Panel and Wings, and our <a href="/en/services/server-administration/">server administration</a>
 service can keep it patched while you focus on the game servers themselves.</p>
]]></content:encoded><category>Gaming</category><category>Panel</category></item><item><title>Databasement: self-hosted database backups, with schedules and restore</title><link>https://dreamserver.ro/en/blog/databasement-database-backup-manager/</link><pubDate>Wed, 01 Jul 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/databasement-database-backup-manager/</guid><description>A hands-on look at Databasement, the open-source database backup manager: MySQL, Postgres, MongoDB and more in one web interface, with scheduling, retention, cross-server restore and S3 or SFTP storage. One container, self-hosted.</description><content:encoded><![CDATA[<p>There is a lesson every administrator learns exactly once, and usually the hard way: a virtual machine snapshot is not a database backup. You restore the VM, the database starts from an inconsistent state in the middle of a transaction, and you discover that &ldquo;we had a backup&rdquo; does not mean &ldquo;I can recover the data&rdquo;. A proper database backup is a logical, consistent dump taken by the database engine itself, kept somewhere off-site, and tested so you know how to restore it. The problem is that doing this for ten databases across five servers, with schedules and retention, quickly turns into a swamp of cron scripts nobody understands anymore.</p>
<p><a href="https://github.com/David-Crty/databasement" rel="nofollow">Databasement</a>
, the open-source database backup manager written by <a href="https://github.com/David-Crty" rel="nofollow">David Courty</a>
, solves exactly that swamp with a single web interface. It is licensed under MIT, has more than 1,800 stars on GitHub, and we run it self-hosted in our Bucharest datacenter. This post is the field report.</p>
<h2 id="what-databasement-actually-is">What Databasement actually is</h2>
<p>Databasement is a <strong>centralized backup manager for databases</strong>. Instead of cron scripts scattered across every server, you have a single web application where you define the database servers, say what to back up and how often, where the backups should go and how long to keep them, and the application handles the rest: it runs the dumps on schedule, compresses them, uploads them to the chosen storage, and gives you a restore button when you need it.</p>
<p>Its tagline, &ldquo;your backups deserve a safe place to live&rdquo;, is not just marketing. The core idea is that a backup is a flow, not a file: it is taken, moved off-site, kept according to a policy, and must be restorable. Databasement models exactly this flow, with visibility on every step.</p>
<h3 id="which-databases-and-destinations-it-covers">Which databases and destinations it covers</h3>
<p>This is where Databasement is surprisingly complete for an open-source project. Out of the box it supports:</p>
<ul>
<li><strong>Databases</strong>: MySQL, MariaDB, PostgreSQL, Microsoft SQL Server, MongoDB, SQLite, Firebird and Redis/Valkey. Basically anything you might run.</li>
<li><strong>Storage destinations</strong>: local, S3-compatible (AWS, MinIO, Backblaze), Azure Blob, Samba/SMB, SFTP and FTP. A backup that stays on the same server is not a backup, and Databasement makes off-site destinations easy.</li>
<li><strong>Compression and encryption</strong>: gzip, zstd, or AES-256 for encrypted backups.</li>
<li><strong>SSH tunnels and remote agents</strong>: to reach databases behind a bastion or in a private network, without exposing the database port.</li>
<li><strong>Notifications</strong>: email, Slack, Discord, Telegram, Pushover, Gotify and webhooks, so you find out immediately when a backup fails.</li>
</ul>
<p>On top of all that, it has access control with SSO (Google, GitHub, GitLab, OpenID Connect), 2FA and role-based permissions, plus a REST API and even an MCP server for automation. It is far more than &ldquo;a cron with a UI&rdquo;.</p>
<h2 id="installing-databasement">Installing Databasement</h2>
<p>Databasement is a Laravel application packaged neatly into a single container. The simplest variant gets you running with SQLite as the internal configuration store, so you do not need a separate database just to run the backup manager:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run -d --name databasement -p 2226:2226 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e DB_CONNECTION<span style="color:#f92672">=</span>sqlite <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e DB_DATABASE<span style="color:#f92672">=</span>/data/database.sqlite <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e ENABLE_QUEUE_WORKER<span style="color:#f92672">=</span>true <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e PUID<span style="color:#f92672">=</span><span style="color:#ae81ff">1000</span> -e PGID<span style="color:#f92672">=</span><span style="color:#ae81ff">1000</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v ./databasement-data:/data <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  davidcrty/databasement:1
</span></span></code></pre></div><p>You bring the container up, open the UI on port 2226, create your admin account on first access, and from there add the database servers and backup jobs from the interface.</p>
<p>One detail that matters a lot and deserves underlining: <strong><code>ENABLE_QUEUE_WORKER=true</code> is not optional if you want scheduled backups</strong>. Databasement runs its scheduler in the same process as the queue worker. Without this flag, the app starts, the UI works, but scheduled jobs never fire, because there is nobody to execute them. It is the classic trap you fall into if you follow a minimalist compose. We run it exactly in this mode, single-container with the worker on, precisely so the scheduler is alive.</p>
<p>As with any tool that holds credentials to all your databases, <strong>do not expose it publicly without authentication</strong>. It has native login, but we also keep it behind our SSO, because an application that can perform destructive restores on production deserves a double fence. At minimum, restrict it with a firewall and put a reverse proxy with TLS in front.</p>
<p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, remember that the backups themselves must land in off-site storage (S3, another server), not on the same disk, otherwise a single incident takes the database and the backup with it.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few things useful beyond the defaults.</p>
<h3 id="the-backup-must-leave-the-server">The backup must leave the server</h3>
<p>The golden rule: a backup on the same disk as the database does not save you from a disk failure or ransomware. Configure an off-site destination (S3, SFTP to another server, SMB) from day one. Databasement makes this part of the job definition, use it.</p>
<h3 id="test-the-restore-not-just-the-backup">Test the restore, not just the backup</h3>
<p>A backup you have never restored is a theory, not a safety net. Use Databasement&rsquo;s cross-server restore to periodically restore a backup into a test database and confirm the data is there. The day you need a restore is not the day you want to discover the dump was corrupt.</p>
<h3 id="tiered-retention">Tiered retention</h3>
<p>Do not keep everything forever, but do not keep only the last copy either. A healthy policy is a few dailies, a few weeklies, a few monthlies. Databasement supports per-job retention policies, exactly so you do not fill your storage while still having historical depth.</p>
<h3 id="ssh-tunnels-for-private-databases">SSH tunnels for private databases</h3>
<p>Never expose port 3306 or 5432 to the internet just so the backup tool can reach it. Databasement connects through an SSH tunnel or a remote agent, so the database stays in its private network and the backup still reaches it.</p>
<h2 id="where-databasement-is-not-the-right-answer">Where Databasement is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not file or whole-machine backup.</strong> Databasement takes database dumps. For files, volumes or VM images you need a different tool (a <a href="/en/services/proxmox-backup/">Proxmox Backup</a>
, restic, borg). The two complement each other.</li>
<li><strong>Not a replacement for replication.</strong> A daily backup gives you a recovery point from yesterday, not real-time failover. For availability you need replication or a cluster; the backup is the safety net, not HA.</li>
<li><strong>The restore is your responsibility to test.</strong> The tool gives you the button, but the confidence only comes from pressing it periodically on real data.</li>
<li><strong>It is another stateful service to maintain.</strong> It has its own SQLite database, its own credentials, its own UI to patch. Worth it, but you treat it as infrastructure, with a backup of its own configuration.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at classic <code>mysqldump</code>/<code>pg_dump</code> scripts plus cron (they work, they are free, but they quickly become a swamp with no visibility, notifications or easy restore), at <a href="https://www.bacula.org/" rel="nofollow">Bacula</a>
 and Bareos (very capable enterprise backup systems, but heavy and file-centric rather than database-centric), and at cloud-native solutions (RDS snapshots and the like, excellent if you are already there, useless self-hosted). For &ldquo;I want a single place where I see and schedule the backups of all my databases, with one-click restore&rdquo;, Databasement is the most on-target.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you manage more than one database and your current backup is a cron script you have not checked in months, Databasement is worth the afternoon of setup. The first time you see in the dashboard all your databases with the last backup green next to each, with schedules and retention and off-site storage, and you know you can restore any of them with one click, you will understand the difference between &ldquo;I hope I have a backup&rdquo; and &ldquo;I know I can recover&rdquo;.</p>
<p>We run Databasement single-container, with the worker on so the scheduler is alive, behind our SSO, for our infrastructure&rsquo;s databases. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 are perfect for a small backup manager, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install, the storage destinations and restore testing if you would rather skip the learning curve.</p>
<p>Either way, getting your database backups in order is the cheapest insurance in IT. The code is on GitHub, there is a public demo you can touch right now, and you will know within an hour whether it earns a spot in your infrastructure.</p>
]]></content:encoded><category>Backup</category><category>Databases</category></item><item><title>Nextcloud: Your Own Dropbox, Minus the Vendor Lock-in</title><link>https://dreamserver.ro/en/blog/nextcloud-self-hosted-cloud/</link><pubDate>Sun, 28 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/nextcloud-self-hosted-cloud/</guid><description>Nextcloud turns a VPS into a full file-sync and collaboration suite you control. What it is, how to install it with AIO, and when it's not the right tool.</description><content:encoded><![CDATA[<p>There&rsquo;s a specific moment every company hits eventually: someone asks exactly where the company&rsquo;s files live, and nobody has a good answer. &ldquo;Google Drive&rdquo; isn&rsquo;t an answer, it&rsquo;s a black box. The files sit somewhere in a US-owned data center, subject to US law regardless of which EU region you picked in the settings, and the pricing tier you&rsquo;re on today isn&rsquo;t guaranteed to exist next year. Add a &ldquo;storage full&rdquo; nag at the worst possible time and the fact that you&rsquo;re paying per seat for something as basic as a shared folder, and the appeal of just running your own becomes obvious.</p>
<p>That&rsquo;s the itch <a href="https://nextcloud.com" rel="nofollow">Nextcloud</a>
 scratches. It&rsquo;s open source (AGPLv3), the code lives on <a href="https://github.com/nextcloud/server" rel="nofollow">GitHub</a>
, and at its core it&rsquo;s a file sync and share platform you install on your own server, backed by your own database, on storage you control. We say this because we actually run it: DreamServer offers managed Nextcloud hosting out of our own Bucharest datacenter, meaning the data doesn&rsquo;t transit through a third party&rsquo;s infrastructure at all, it sits on our storage, behind our network, inside Romania. For companies with GDPR obligations, or public sector and healthcare clients who need to point at a specific physical location and say &ldquo;that&rsquo;s where it is,&rdquo; that distinction matters, not as a compliance checkbox but as a real answer to a real question.</p>
<h2 id="what-nextcloud-actually-is">What Nextcloud actually is</h2>
<p>Calling Nextcloud &ldquo;a Dropbox clone&rdquo; undersells it. Files is the core app: desktop clients for Windows/macOS/Linux, mobile apps for iOS/Android, and a web UI, keeping folders in sync in real time with the usual conflict handling and offline queueing. But Files is just the entry point into an app ecosystem that turns the same install into a fairly complete internal collaboration platform:</p>
<ul>
<li><strong>Talk</strong> for chat, voice, and video calls (including group calls), useful if you want your team communicating on infrastructure you control rather than a SaaS chat tool.</li>
<li><strong>Calendar</strong> and <strong>Contacts</strong>, both speaking standard CalDAV/CardDAV, so they sync with Thunderbird, Apple Calendar, or any other standards-compliant client.</li>
<li><strong>Mail</strong>, a webmail client living inside the same interface, plus <strong>Deck</strong>, a Trello-style kanban board for lightweight project tracking.</li>
<li><strong>Notes</strong>, plus dozens more apps in the built-in app store covering everything from forms to whiteboards.</li>
<li>Online document editing through integration with <strong>Collabora Online</strong> or <strong>OnlyOffice</strong>, both separate open source projects that plug into Nextcloud so you can co-edit a Word or Excel file in the browser without ever touching Microsoft 365.</li>
</ul>
<p>On storage, Nextcloud isn&rsquo;t limited to its own disk: you can mount S3-compatible object storage, FTP, SMB/CIFS shares, or other WebDAV servers as external storage, so it can sit in front of storage you already have rather than forcing a migration.</p>
<h3 id="why-it-matters">Why it matters</h3>
<p>Strip away the app list and the actual pitch is data sovereignty. When you run Nextcloud yourself, or have someone run it for you, instead of handing files to Google Workspace or Microsoft 365, you know exactly which server, which datacenter, which country your data sits in. For a Romanian or EU company with GDPR obligations, that&rsquo;s not a nice-to-have, it&rsquo;s the difference between a straightforward compliance answer and a lengthy explanation involving Standard Contractual Clauses and a lawyer. It&rsquo;s why we built this into our own service lineup: our <a href="/en/services/nextcloud-storage/">Nextcloud storage hosting</a>
 runs on infrastructure we own end to end, so the answer to &ldquo;where&rsquo;s the data&rdquo; is &ldquo;in our Bucharest datacenter,&rdquo; full stop.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>Once it&rsquo;s running, day-to-day Nextcloud gives you a fairly complete replacement for the file-sharing habits most teams have picked up from consumer cloud tools, minus the seat-based pricing anxiety:</p>
<ul>
<li><strong>Sync clients</strong> for desktop and mobile that keep local folders mirrored against the server, with selective sync so you&rsquo;re not forced to mirror everything to every device.</li>
<li><strong>Share links</strong> with configurable expiry dates, optional passwords, and permission levels (view-only, upload-only, edit), so sending a client a file doesn&rsquo;t mean emailing an attachment into the void.</li>
<li><strong>File versioning</strong>, so an overwritten or accidentally-deleted file has a history you can roll back to, plus <strong>Talk</strong> for video calls when you want a call link that doesn&rsquo;t depend on a third-party account.</li>
<li><strong>CalDAV/CardDAV sync</strong> for calendars and contacts across every device and client that speaks those (very ordinary, very well-supported) protocols.</li>
<li><strong>Collaborative document editing</strong> in the browser through Collabora or OnlyOffice, with multiple people editing the same document simultaneously, plus <strong>external storage mounting</strong> for S3, FTP, SMB, and WebDAV backends.</li>
<li><strong>Two-factor authentication</strong> for logins, and an <strong>activity/audit log</strong> so you can see who touched what and when, which matters the moment you need to answer &ldquo;who deleted this file&rdquo; honestly.</li>
</ul>
<h2 id="installing-nextcloud-on-a-vps">Installing Nextcloud on a VPS</h2>
<p>The deployment path Nextcloud itself recommends today is <strong>Nextcloud All-in-One (AIO)</strong>, a Docker-based setup where a single mastercontainer manages Nextcloud, the database, Collabora, and the other moving parts together, and gives you a web UI to configure the rest. It&rsquo;s a meaningful improvement over hand-rolling a LEMP stack with a dozen PHP extensions, because AIO handles updates and the interdependencies between components for you.</p>
<p>On a fresh VPS with Docker installed, the mastercontainer starts like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker run <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --init <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --sig-proxy<span style="color:#f92672">=</span>false <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name nextcloud-aio-mastercontainer <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart always <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --publish 8080:8080 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --env APACHE_PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">11000</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --env APACHE_IP_BINDING<span style="color:#f92672">=</span>127.0.0.1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --volume /var/run/docker.sock:/var/run/docker.sock:ro <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  nextcloud/all-in-one:latest
</span></span></code></pre></div><p>That exposes the AIO web interface on port 8080, where you set an admin passphrase and get walked through pulling and starting the Nextcloud, database, and Collabora containers. From there you point a reverse proxy (or AIO&rsquo;s own built-in one) at it for the real domain and TLS certificate.</p>
<p>Firewall-wise, the rule of thumb is: expose only what needs to be public, and close the setup port once configuration is finished.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 443/tcp        <span style="color:#75715e"># HTTPS, the actual Nextcloud interface</span>
</span></span><span style="display:flex;"><span>ufw allow 8080/tcp       <span style="color:#75715e"># AIO setup UI, restrict to your IP or close after setup</span>
</span></span><span style="display:flex;"><span>ufw deny 11000/tcp       <span style="color:#75715e"># internal Apache port, never expose this directly</span>
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>This is a workload that fits comfortably on a mid-tier <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
: the mastercontainer, database, and Collabora together aren&rsquo;t heavy for a small team, though you&rsquo;ll want to size RAM up if you&rsquo;re running Collabora for a lot of simultaneous document editors.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="collabora-vs-onlyoffice">Collabora vs OnlyOffice</h3>
<p>Both plug into Nextcloud as separate services and let you co-edit Office documents in the browser. Collabora (LibreOffice&rsquo;s rendering engine) tends to have better fidelity for complex ODF/OOXML formatting and is what AIO bundles by default. OnlyOffice&rsquo;s UI more closely mimics Microsoft Office, and some teams find that transition easier. Neither is objectively &ldquo;better,&rdquo; it&rsquo;s fidelity versus familiarity, and both run as their own containers alongside Nextcloud.</p>
<h3 id="external-storage-as-primary-storage">External storage as primary storage</h3>
<p>For larger deployments, mounting S3-compatible object storage as the primary storage backend, rather than local disk, is a common pattern: it decouples storage growth from the VM&rsquo;s disk and lets you scale storage independently of compute. This is configured per-storage in the External Storage app rather than as a wholesale replacement, so you can mix local and object storage across different folders.</p>
<h3 id="server-side-encryption-vs-e2ee">Server-side encryption vs E2EE</h3>
<p>Server-side encryption-at-rest encrypts files on disk so raw disk access doesn&rsquo;t expose plaintext, useful against physical theft, but the server itself still holds the keys and can decrypt on the fly. The separate End-to-End Encryption app is stricter: specific folders where even the server operator can&rsquo;t read contents, at the cost of losing features like full-text search on those folders. Default to server-side encryption and reach for E2EE only where it&rsquo;s genuinely needed.</p>
<h3 id="redis-for-locking-and-caching">Redis for locking and caching</h3>
<p>Nextcloud uses file locking to prevent two clients from corrupting the same file during simultaneous edits, and by default that locking, along with general caching, can fall back to slower mechanisms. Wiring in Redis for both is a well-documented performance win once you have more than a handful of concurrent users, and it&rsquo;s included in the AIO stack by default.</p>
<h3 id="backing-up-the-aio-stack">Backing up the AIO stack</h3>
<p>Because AIO manages its containers and volumes as a unit, backup strategy needs to account for the mastercontainer&rsquo;s own backup tooling, accessible through its UI, rather than snapshotting individual containers ad hoc. Treat the whole stack, database included, as one consistent backup unit, and test a restore before you need one for real.</p>
<h2 id="where-nextcloud-is-not-the-right-answer">Where Nextcloud is not the right answer</h2>
<p>Nextcloud is not a good fit for every case. If you&rsquo;re two people who just want a shared folder that syncs, running a full server stack (web server, database, Redis, possibly Collabora) is real overhead for a problem a hosted consumer product solves with an app install and no server to think about. The AIO stack, while simpler than manual setup, is still multiple containers you&rsquo;re responsible for patching and backing up, an ongoing cost, not a one-time task.</p>
<p>At the other end, thousands of concurrent editors and deep integration with an existing enterprise identity stack may need tooling depth a self-managed install doesn&rsquo;t match out of the box (Nextcloud sells enterprise support for exactly this reason). And because the core is PHP, tuning it for very large user counts, PHP-FPM pool sizing, opcache, query performance, is its own discipline that gets fiddly without someone who&rsquo;s done it before.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://www.seafile.com" rel="nofollow">Seafile</a>
</strong>: a leaner, more narrowly-focused file sync and share tool. If all you need is fast, reliable file sync without the wider app ecosystem, Seafile&rsquo;s smaller footprint is worth a look, though you give up Talk, Deck, and the built-in office suite integration.</li>
<li><strong><a href="https://owncloud.com" rel="nofollow">ownCloud</a>
</strong>: the project Nextcloud forked from in 2016. Development has diverged since, and Nextcloud has pulled ahead in app ecosystem breadth and release cadence, but ownCloud remains a legitimate, actively developed alternative if it fit your needs previously.</li>
<li><strong>Google Workspace / Microsoft 365</strong>: the honest non-self-hosted comparison. Both are more convenient out of the box, someone else handles the operations, and neither requires you to think about servers. The tradeoff is the one we opened with: your data sits on infrastructure you don&rsquo;t control, in jurisdictions you don&rsquo;t choose, at a per-seat price that can and does change.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If data residency, GDPR, or just not wanting to pay per-seat forever is a real concern for your organization, Nextcloud is a solid, mature answer, not a compromise. The AIO deployment path has made it considerably easier to stand up, and the app ecosystem means you&rsquo;re likely replacing more than one SaaS subscription with a single self-hosted stack.</p>
<p>If you&rsquo;d rather not run the stack yourself, that&rsquo;s exactly what our <a href="/en/services/nextcloud-storage/">Nextcloud storage hosting</a>
 is for: managed Nextcloud on our own infrastructure in Bucharest, data residency handled, updates handled. If you want to run it yourself but on infrastructure that doesn&rsquo;t disappear at the whim of a hyperscaler&rsquo;s pricing team, a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
 is a straightforward place to put the AIO stack, and if you&rsquo;d rather hand off the ongoing patching without giving up control, our <a href="/en/services/server-administration/">server administration</a>
 service covers that gap.</p>
]]></content:encoded><category>Cloud</category><category>Storage</category></item><item><title>Graylog: Open Source Log Management That Doesn't Make You Grep Six Servers at 3am</title><link>https://dreamserver.ro/en/blog/graylog-log-management/</link><pubDate>Wed, 24 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/graylog-log-management/</guid><description>A practical look at Graylog: architecture, streams, pipeline rules, alerting, a real docker-compose setup, and when it's the wrong tool for the job.</description><content:encoded><![CDATA[<p>It&rsquo;s 3am, something is broken, and you have a dozen servers. You SSH into the first one, <code>tail -f /var/log/nginx/error.log</code>, see nothing useful, SSH into the second one, repeat, SSH into the third one and start questioning your career choices. By the time you&rsquo;ve grepped through server number seven, whatever caused the spike has already resolved itself, and you&rsquo;re left with a pile of terminal tabs and no idea what actually happened.</p>
<p>This is exactly the problem centralized logging exists to solve, and it&rsquo;s why a tool like <a href="https://www.graylog.org" rel="nofollow">Graylog</a>
 has stuck around for over a decade. Graylog is an open source log management and analysis platform: it ingests logs from everything you run, stores them in a searchable index, and layers streams, dashboards, and alerting on top. The code lives at <a href="https://github.com/Graylog2/graylog2-server" rel="nofollow">github.com/Graylog2/graylog2-server</a>
; the core server ships under SSPL with some components under GPL, so licensing is a &ldquo;check the specific component&rdquo; situation rather than one blanket answer. We don&rsquo;t run Graylog ourselves and don&rsquo;t offer it as a managed service, but it&rsquo;s a genuinely solid option worth a proper look if you&rsquo;re evaluating what to self-host for logs.</p>
<h2 id="what-graylog-actually-is">What Graylog actually is</h2>
<p>Graylog isn&rsquo;t a single binary, it&rsquo;s three pieces working together. The <strong>Graylog server</strong> is the brain: it receives incoming messages, applies whatever routing and processing rules you&rsquo;ve configured, and serves the web UI and REST API. Under it sits <strong>OpenSearch or Elasticsearch</strong>, doing the actual heavy lifting of storing and indexing every message so you can search millions of lines in under a second, the same technology stack behind the &ldquo;ELK&rdquo; world. Alongside that sits <strong>MongoDB</strong>, easy to overlook but doing something different: it stores configuration and metadata, users, dashboards, stream definitions, alert conditions, index sets, everything describing how your instance is set up. Your logs go into OpenSearch; the rules about what happens to them live in MongoDB. This split is a defining trait of Graylog&rsquo;s design, and it means you&rsquo;re operating three services, not one.</p>
<p>Logs get in through <strong>inputs</strong>: Syslog (UDP or TCP), GELF (Graylog Extended Log Format, their own structured format built to carry more than a flat syslog line can), Beats (so Filebeat, Winlogbeat, and the rest of the Elastic Beats family ship straight into it), raw TCP/UDP, HTTP, and a handful of others. Whatever is already emitting logs on your infrastructure almost certainly has a way in.</p>
<h3 id="why-it-matters">Why it matters</h3>
<p>When an incident spans multiple hosts, containers, or services, the question is never &ldquo;what did server A log,&rdquo; it&rsquo;s &ldquo;what happened, in order, across everything, in the two minutes around the failure.&rdquo; Grepping per-host doesn&rsquo;t answer that; a shared, searchable, time-correlated index does. It also means logs survive the machine that produced them: if a container gets OOM-killed and rescheduled, its logs are already somewhere else.</p>
<p>Centralized logging and metrics-based monitoring solve adjacent but different problems: metrics tell you something is wrong and roughly when, logs tell you what actually happened and why. Worth reading <a href="https://dreamserver.ro/en/blog/grafana-open-source-monitoring-dashboards/">our Grafana post</a>
 on the metrics side of that pairing. A mature setup tends to run both: Grafana for the &ldquo;is something wrong right now&rdquo; dashboards and alerts, Graylog for the &ldquo;let&rsquo;s find out exactly what happened&rdquo; investigation afterward.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p><strong>Streams</strong> route messages into logical buckets in real time based on rules you define, source, facility, a field value, a regex match. A stream isn&rsquo;t a copy of the data, it&rsquo;s a live filter letting a team or app see only its own slice of the firehose. Realistic example: route anything from your nginx inputs where <code>response_code</code> is 5xx into an &ldquo;nginx-errors&rdquo; stream, so the on-call dashboard only shows things actually broken.</p>
<p><strong>Pipeline rules</strong> are where the real processing power lives, a small rule-based language for parsing, enriching, rewriting, or dropping messages as they arrive. Common pattern: pull an IP from an unstructured log line with a regex, do a GeoIP lookup, attach the country as a new field, before the message is indexed.</p>
<p><strong>Extractors</strong> are the simpler sibling: regex or grok-style field extraction defined directly on an input, good for straightforward single-field pulls that don&rsquo;t need the full pipeline language.</p>
<p><strong>Search</strong> is where you&rsquo;ll live day to day: a query language over indexed messages, with time range pickers and field statistics, backed by OpenSearch so it stays fast at real volume. <strong>Dashboards</strong> are built from saved searches and aggregations, error rate over time, top talkers by source IP, request volume by status code, arranged on one screen. <strong>Alerting</strong> runs on event definitions, conditions evaluated against your data (say, more than 50 events matching a stream in a 5-minute window) that trigger a notification, email, webhook, or other integration. Typical example: alert when the error rate in &ldquo;nginx-errors&rdquo; crosses a threshold in a rolling window, so you find out from Graylog instead of from a customer.</p>
<p>Graylog also sees a fair amount of security and SIEM-adjacent use: correlating events across systems, alerting on suspicious patterns. It&rsquo;s genuinely useful there, but a full SIEM typically wants more than open source Graylog ships with, case management, curated detection content, threat intel feeds, which is part of what the commercial Graylog Security tier adds.</p>
<h2 id="installing-graylog-on-a-vps">Installing Graylog on a VPS</h2>
<p>Graylog publishes official Docker images for itself, OpenSearch, and MongoDB, so Compose is the fastest realistic path to a working instance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">mongodb</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#e6db74">&#34;mongo:6.0&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;mongo_data:/data/db&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restart</span>: <span style="color:#e6db74">&#34;on-failure&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">opensearch</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#e6db74">&#34;opensearchproject/opensearch:2.19.1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;bootstrap.memory_lock=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;discovery.type=single-node&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;action.auto_create_index=false&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;plugins.security.ssl.http.enabled=false&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;plugins.security.disabled=true&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ulimits</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">memlock</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">soft</span>: -<span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">hard</span>: -<span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;opensearch_data:/usr/share/opensearch/data&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restart</span>: <span style="color:#e6db74">&#34;on-failure&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">graylog</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#e6db74">&#34;graylog/graylog:6.1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">GRAYLOG_PASSWORD_SECRET</span>: <span style="color:#e6db74">&#34;changeme-to-a-long-random-string&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">GRAYLOG_ROOT_PASSWORD_SHA2</span>: <span style="color:#e6db74">&#34;put-a-sha256-hash-of-your-admin-password-here&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">GRAYLOG_HTTP_EXTERNAL_URI</span>: <span style="color:#e6db74">&#34;https://logs.example.com/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">entrypoint</span>: <span style="color:#e6db74">&#34;/usr/bin/tini -- wait-for-it opensearch:9200 --  /docker-entrypoint.sh&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;9000:9000/tcp&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;1514:1514/tcp&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;1514:1514/udp&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;12201:12201/tcp&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;12201:12201/udp&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">depends_on</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;mongodb&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;opensearch&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;graylog_data:/usr/share/graylog/data&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restart</span>: <span style="color:#e6db74">&#34;on-failure&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">mongo_data</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">opensearch_data</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">graylog_data</span>:
</span></span></code></pre></div><p>Generate the SHA256 root password hash before you start it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo -n <span style="color:#e6db74">&#34;your-real-admin-password&#34;</span> | sha256sum
</span></span></code></pre></div><p>Bring it up, wait for OpenSearch and MongoDB to finish their first boot, then hit port 9000:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose up -d
</span></span><span style="display:flex;"><span>docker compose logs -f graylog
</span></span></code></pre></div><p>Once it&rsquo;s reachable, lock the ports down instead of leaving everything open. You want the web UI reachable only from your admin network or behind a TLS reverse proxy, and log-shipping ports open only to the hosts that actually send you logs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow from 203.0.113.0/24 to any port <span style="color:#ae81ff">9000</span> proto tcp comment <span style="color:#e6db74">&#34;Graylog web UI, admin network only&#34;</span>
</span></span><span style="display:flex;"><span>ufw allow from 10.0.0.0/16 to any port <span style="color:#ae81ff">1514</span> proto tcp comment <span style="color:#e6db74">&#34;Syslog TCP, internal hosts&#34;</span>
</span></span><span style="display:flex;"><span>ufw allow from 10.0.0.0/16 to any port <span style="color:#ae81ff">1514</span> proto udp comment <span style="color:#e6db74">&#34;Syslog UDP, internal hosts&#34;</span>
</span></span><span style="display:flex;"><span>ufw allow from 10.0.0.0/16 to any port <span style="color:#ae81ff">12201</span> comment <span style="color:#e6db74">&#34;GELF, internal hosts&#34;</span>
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">9000</span>
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">1514</span>
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">12201</span>
</span></span></code></pre></div><p>This is exactly the kind of workload a VPS handles well: not huge on its own, but running three services means you want a machine with real, dedicated memory rather than a burstable micro instance where OpenSearch will fight you for RAM. A <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
 with a few gigabytes to spare is a reasonable start for small to medium log volume; scale the OpenSearch heap and disk as retention needs grow.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p><strong>Index rotation and retention.</strong> Graylog organizes indices into &ldquo;index sets&rdquo; with rotation strategies (by size or time) and retention strategies deciding when old indices close, archive, or get deleted. Get this wrong and you either burn disk on data nobody looks at, or rotate so aggressively you lose the history you needed during an incident review. Set retention deliberately per index set, not as an afterthought once disk alerts start firing.</p>
<p><strong>Streams as team and application boundaries.</strong> Rather than one giant firehose everyone stares at, use streams to give each team or app its own slice, a &ldquo;database&rdquo; stream, a &ldquo;web&rdquo; stream, a &ldquo;security&rdquo; stream. Streams can carry their own retention settings and access permissions, so a team gets visibility into its own logs without the whole cluster&rsquo;s data.</p>
<p><strong>Pipeline rules versus extractors.</strong> Extractors are simpler and live on the input itself, good for a quick single-field pull with no conditional logic. Pipeline rules are the right tool once you need conditionals, multiple transformations, GeoIP or lookup-table enrichment, or routing decisions based on parsed content. Rule of thumb: if you&rsquo;re writing more than one extractor to handle variations of the same message, move that logic into a pipeline rule instead.</p>
<p><strong>Securing the web interface.</strong> Graylog ships its own user and role system, but beyond a single admin, plan access control deliberately: separate admin accounts from read-only viewer roles, and don&rsquo;t expose port 9000 to the open internet without TLS in front of it. If you already run centralized auth elsewhere, check Graylog&rsquo;s authentication provider options before adding local users.</p>
<h2 id="where-graylog-is-not-the-right-answer">Where Graylog is not the right answer</h2>
<p>If you&rsquo;re running one or two servers, <code>journalctl</code> and rotated log files with <code>grep</code>/<code>awk</code> genuinely cover most needs, and running Graylog, OpenSearch, and MongoDB just to watch logs from one box is a lot of operational overhead for very little payoff.</p>
<p>If your team already runs an Elastic/OpenSearch stack for other purposes (product search, analytics), adding Graylog on top means a second thing to operate against data infrastructure you already have, and it may make more sense to build log ingestion directly into what you&rsquo;ve got.</p>
<p>Resource overhead is real: three services, each with their own memory and disk footprint, is meaningfully more than a single log aggregator binary, and OpenSearch wants real memory to stay fast. For small workloads this can be disproportionate to the problem you&rsquo;re solving.</p>
<p>And if you actually need a SIEM with compliance-grade case management, curated detection content, and threat intelligence feeds, the open source edition alone won&rsquo;t get you there; that&rsquo;s the gap the commercial Graylog Security tier exists to fill.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><strong>The Elastic Stack</strong> (<a href="https://www.elastic.co" rel="nofollow">elastic.co</a>
), Elasticsearch, Logstash, Kibana, is the platform Graylog&rsquo;s own storage layer is adjacent to. If you&rsquo;re already invested in Elastic tooling or need Kibana&rsquo;s visualization ecosystem, running the stack directly rather than through Graylog&rsquo;s abstraction is reasonable, though you lose Graylog&rsquo;s ops-friendly streams and pipeline UI and take on Elastic&rsquo;s own licensing considerations.</p>
<p><strong>Loki with Grafana</strong> (<a href="https://grafana.com/oss/loki/" rel="nofollow">grafana.com/oss/loki</a>
) indexes only metadata labels instead of full log content, treating log bodies as compressed, unindexed blobs, dramatically cheaper to run at scale. The tradeoff is search flexibility: broad full-text search isn&rsquo;t Loki&rsquo;s strong suit the way it is Graylog&rsquo;s. If you&rsquo;re already all-in on Grafana for metrics, Loki slots in naturally without adding a new infrastructure paradigm.</p>
<p><strong>SigNoz</strong> is a newer open source observability platform bundling logs, metrics, and traces on top of ClickHouse, appealing if you want one platform covering all three signals from day one, though it&rsquo;s a younger project with a smaller community and less operational history than Graylog or the Elastic stack.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;ve got more than a couple of servers and you&rsquo;re tired of SSHing around at 3am reconstructing what happened, centralized logging earns its keep fast, and Graylog is a mature, capable way to do it: solid ingestion options, a real processing pipeline, search that stays usable at volume. Go in with eyes open about the three-service footprint, size OpenSearch properly for your log volume, and decide your retention strategy before disk usage forces the decision for you.</p>
<p>If you&rsquo;re weighing where to run this, a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
 gives you the dedicated resources this workload wants without over-provisioning for a small deployment, and if you&rsquo;d rather not own the operational side of keeping Docker, OpenSearch heap sizes, and index retention healthy long-term, our <a href="/en/services/server-administration/">server administration services</a>
 cover exactly that kind of ongoing care.</p>
]]></content:encoded><category>Observability</category><category>Logs</category></item><item><title>Akvorado: see who is actually sending you traffic, flow by flow</title><link>https://dreamserver.ro/en/blog/akvorado-network-flow-analytics/</link><pubDate>Sun, 21 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/akvorado-network-flow-analytics/</guid><description>A hands-on look at Akvorado, the open-source flow collector from Free: it receives NetFlow/IPFIX and sFlow, enriches with SNMP and GeoIP, stores in ClickHouse and draws Sankey diagrams. Peering, capacity and DDoS analysis, self-hosted.</description><content:encoded><![CDATA[<p>The question that haunts every network operator is simple and hard to answer: who is actually sending me all this traffic? The counter on your uplink tells you 8 Gbps are coming in, but it does not tell you that half of it comes from a single AS you could peer with directly and cut your transit bill. Without flow visibility, peering and capacity decisions are informed guessing. With it, they are arithmetic.</p>
<p><a href="https://github.com/akvorado/akvorado" rel="nofollow">Akvorado</a>
, the open-source flow collector built and run in production by <a href="https://www.free.fr/" rel="nofollow">Free</a>
 (a large French ISP), exists for exactly that question. It is licensed under AGPLv3, has more than 2,300 stars on GitHub, and we run it on our own network, in our Bucharest datacenter, in front of AS57050 and our DSIX exchange. This post is the field report.</p>
<h2 id="what-akvorado-actually-is">What Akvorado actually is</h2>
<p>Akvorado is a <strong>network flow collector, enricher and visualizer</strong>. Your routers and switches export metadata about traffic (who talks to whom, on which ports, how much) via standard protocols: <strong>NetFlow, IPFIX and sFlow</strong>. Akvorado receives them, adds context, stores them in a database optimized for aggregation, and gives you a web console where you answer questions about your traffic in seconds.</p>
<p>The &ldquo;enrichment&rdquo; part is what turns a raw flow into useful information. A flow record says &ldquo;IP X sent 4 GB to IP Y on interface 12&rdquo;. Not very useful. Akvorado adds:</p>
<ul>
<li><strong>Interface names via SNMP</strong>: interface 12 becomes &ldquo;transit: telia&rdquo; or &ldquo;peering: franceix&rdquo;, with description and speed, by querying the router over SNMP.</li>
<li><strong>Geo and ASN</strong>: IP X becomes &ldquo;AS2906 Netflix&rdquo; with a country, using GeoIP databases (IPinfo).</li>
<li><strong>BGP context</strong>: AS-path, communities, boundary type (internal vs external).</li>
</ul>
<p>Out of an anonymous flow comes a readable sentence: &ldquo;AS2906 Netflix sent me 4.7 Gbps through the telia transit, exiting on th2-edge1&rdquo;. Exactly the kind of sentence you use to make decisions.</p>
<h3 id="how-it-is-built-underneath">How it is built underneath</h3>
<p>Akvorado is not a single binary, it is a small scale-oriented architecture, and it is worth understanding in three pieces:</p>
<ul>
<li><strong>Inlet</strong>: the process that listens for flow packets (UDP) and decodes them. This is where sFlow and NetFlow from routers land.</li>
<li><strong>Orchestrator</strong>: the configuration brain, it manages the ClickHouse schema and Kafka topics, and serves configuration to the other components.</li>
<li><strong>Console</strong>: the web interface, where you, the human, ask questions and see graphs.</li>
</ul>
<p>Behind the scenes, flows pass through a <strong>Kafka</strong> (a buffer that absorbs bursts) and land in <strong>ClickHouse</strong>, the columnar database that can aggregate billions of flow rows in milliseconds. That choice is the key: a flow analyzer lives or dies on how fast it answers &ldquo;sum all my traffic over the last 24 hours, grouped by source AS&rdquo;. ClickHouse is monstrously good at exactly that.</p>
<h2 id="what-you-see-in-the-console">What you see in the console</h2>
<p>The console is where Akvorado shines. You pick a few <strong>dimensions</strong> (source AS, exporter name, inbound provider, country, port) and a <strong>filter</strong> in a simple language (<code>InIfBoundary = external</code>), and Akvorado draws you:</p>
<ul>
<li><strong>A Sankey diagram</strong>: columns left to right showing how traffic flows, from the source AS, through the connection type (transit / peering / ix), through the concrete provider (telia, cogent, franceix), to the edge router. At a glance you see what proportion of traffic comes from where and by which path it exits. It is the best picture for peering decisions you will ever see.</li>
<li><strong>Time series</strong>: the same traffic drawn over time, so you see peaks, day/night patterns and events.</li>
<li><strong>A table</strong>: the exact numbers, sortable, behind the graphs.</li>
</ul>
<p>For an ISP or a LIR, this answers questions worth real money: &ldquo;how much transit would I save if I peered directly with this AS?&rdquo;, &ldquo;who are my top 10 talkers?&rdquo;, &ldquo;is that weird 3 AM traffic a legitimate backup or the start of an attack?&rdquo;.</p>
<h2 id="installing-akvorado">Installing Akvorado</h2>
<p>Akvorado is not a one-line service, precisely because it brings ClickHouse, Kafka and Redis with it. The good news: the official repo ships a complete <code>docker compose</code> that brings the whole stack up with one command. Clone, configure, bring up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/akvorado/akvorado
</span></span><span style="display:flex;"><span>cd akvorado/docker
</span></span><span style="display:flex;"><span>cp ../config/akvorado.yaml akvorado.yaml
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>The compose starts ClickHouse, Kafka, Redis, plus the three Akvorado components. The console becomes available on the configured port (via the proxy in the compose by default), and the public demo at <a href="https://demo.akvorado.net/" rel="nofollow">demo.akvorado.net</a>
 shows you exactly what it looks like with real data, before you invest in your own setup.</p>
<p>The important step once it is up: <strong>tell your routers to export flows to Akvorado</strong>. On a router you configure sFlow or NetFlow toward the inlet&rsquo;s IP and port. In our case, the edge routers send sFlow to Akvorado on a dedicated port, separate from the rest of our telemetry pipeline, so they do not step on each other.</p>
<p>As with any management console, <strong>do not expose it publicly without an authentication layer</strong>, because it reveals your network&rsquo;s topology and traffic patterns. We put it behind our SSO; you, at minimum, restrict it with a firewall:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow from YOUR_OFFICE_IP to any port <span style="color:#ae81ff">8080</span> proto tcp
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">8080</span>
</span></span></code></pre></div><p>The flow ingestion port (UDP), on the other hand, must be open toward your routers, and only toward them, so nobody can send you bogus flows.</p>
<p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, ClickHouse will want some disk and RAM, so size it for the retention you want.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few things useful beyond the defaults.</p>
<h3 id="interface-descriptions-are-everything">Interface descriptions are everything</h3>
<p>The power of the Sankey lives in the interface names. If your routers have clearly described interfaces (<code>transit: X</code>, <code>peering: Y</code>, <code>ix: DSIX</code>), Akvorado picks them up via SNMP and the graphs become instantly readable. Invest ten minutes in good descriptions on your routers and you gain months of clear reports.</p>
<h3 id="use-it-for-peering-decisions">Use it for peering decisions</h3>
<p>The most valuable report you can pull: group by source AS, filter to traffic coming in via transit (not via existing peering), and sort descending. The top ASes in the list are your peering candidates, the ones where, if you connect directly at an exchange like DSIX, you move volume off paid transit onto free peering.</p>
<h3 id="retention-costs-disk">Retention costs disk</h3>
<p>ClickHouse compresses excellently, but high-resolution flows over the long term still take space. Keep fine resolution short-term (the last few weeks) and aggregates long-term. Akvorado supports multi-resolution tables for exactly this.</p>
<h3 id="separate-ingestion-from-the-rest-of-your-telemetry">Separate ingestion from the rest of your telemetry</h3>
<p>If you already have an sFlow collector for something else (in our case DDoS protection), run Akvorado on a different ingestion port. A router can export the same flow to multiple collectors at once, each with its own purpose.</p>
<h2 id="where-akvorado-is-not-the-right-answer">Where Akvorado is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not health monitoring.</strong> Akvorado tells you about traffic, not the router&rsquo;s CPU, temperature or a link&rsquo;s state. For that you need classic SNMP and a <a href="/en/blog/grafana-open-source-monitoring-dashboards/">Grafana</a>
 on top.</li>
<li><strong>Not packet capture.</strong> Flows are sampled and aggregated; you see &ldquo;AS2906 sent me 4 GB&rdquo;, not packet contents. For packet-level forensics you need a different tool.</li>
<li><strong>Requires hardware that exports flows.</strong> If your routers do not support NetFlow/IPFIX/sFlow, Akvorado has nothing to collect.</li>
<li><strong>ClickHouse + Kafka are not &ldquo;lightweight&rdquo;.</strong> It is a real stack, not a binary. On a small network it runs comfortably, but you treat it as infrastructure, with backups and tuning, not as a container you throw and forget.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at <a href="https://www.ntop.org/products/traffic-analysis/ntop/" rel="nofollow">ntopng</a>
 (excellent for real-time visibility and deep packet inspection, but less oriented toward historical analysis at scale), <a href="http://www.pmacct.net/" rel="nofollow">pmacct</a>
 (the classic, extremely flexible collection engine, but no UI, you build the visualization), and <a href="https://www.elastiflow.com/" rel="nofollow">Elastiflow</a>
 on the Elastic stack (capable, but heavier and with commercial parts). For an ISP that specifically wants peering and traffic analysis with readable Sankey diagrams, Akvorado is the most on-target.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you operate a network with transit and peering, or even just a router that traffic you want to understand passes through, Akvorado is worth the setup weekend. The first time you pull a Sankey and see in black and white that 40% of your paid transit comes from three ASes you could peer with for free at a local exchange, you will know it has earned a permanent place in your stack.</p>
<p>We run Akvorado in front of the AS57050 network, fed by our edge routers, and use it exactly for peering decisions at <a href="/en/dsix/">DSIX</a>
, our Bucharest exchange. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 are large enough to run a test ClickHouse, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install and tuning if you would rather skip the learning curve.</p>
<p>Either way, finally seeing who sends you your traffic is a revelation the bytes graph on your uplink will never give you. The code is on GitHub, there is a public demo you can touch right now, and you will know within an hour whether it earns a spot in front of your network.</p>
]]></content:encoded><category>Network</category><category>Observability</category></item><item><title>authentik: The Open-Source SSO Platform That Speaks SAML, OIDC, LDAP, and RADIUS</title><link>https://dreamserver.ro/en/blog/authentik-open-source-sso/</link><pubDate>Wed, 17 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/authentik-open-source-sso/</guid><description>A practical look at authentik, the open-source identity provider with SAML, OIDC, LDAP, SCIM and RADIUS support, configurable flows, and forward-auth for any self-hosted app.</description><content:encoded><![CDATA[<p>If you run more than three self-hosted apps, you already know the feeling: every single one has its own account, its own password, its own MFA situation, when it bothers to have one at all. A Nextcloud here, a Grafana there, an internal dashboard next to a wiki and a VPN. Eventually you either start reusing the same password everywhere, which defeats the whole point, or you end up with a password manager holding fifteen entries just for your own tools. &ldquo;One login for everything&rdquo; is appealing precisely because it fixes the problem at the root: one place to manage users, one place to enforce MFA, one place to revoke access when someone leaves. The catch is that getting there means introducing a new, critical piece of infrastructure: a self-hosted identity provider (IdP) that everything else now depends on.</p>
<p>authentik (<a href="https://github.com/goauthentik/authentik" rel="nofollow">GitHub repo</a>
, official site at <a href="https://goauthentik.io" rel="nofollow">goauthentik.io</a>
) is exactly that: a self-hosted, open-source identity provider and SSO platform built to cover a wide range of authentication protocols instead of just one. It isn&rsquo;t a thin proxy that slaps a login page in front of an app; it&rsquo;s a full identity system, with users, groups, policies, and configurable authentication flows.</p>
<h2 id="what-authentik-actually-is">What authentik actually is</h2>
<p>authentik doesn&rsquo;t try to be just one thing: it speaks SAML, OAuth2/OpenID Connect (OIDC), LDAP, SCIM for automated account provisioning, and RADIUS for network- and VPN-level authentication. That covers the four different ways modern (and not-so-modern) systems talk about &ldquo;who are you&rdquo;: web apps that understand OIDC or SAML, legacy services expecting a classic LDAP directory, provisioning systems that speak SCIM, and network gear expecting RADIUS. Instead of running three or four separate tools for each of these, you run one that speaks all of them.</p>
<p>What really sets it apart, though, isn&rsquo;t the protocol list, it&rsquo;s the &ldquo;flows and stages&rdquo; engine. Instead of a hardcoded login screen, a fixed registration page, and a rigid password-recovery process, authentik models each of these as a configurable &ldquo;flow&rdquo; built from reusable &ldquo;stages&rdquo;: an identification stage, a password-verification stage, an MFA stage, a consent stage, and so on. You can build a different login flow for one group of users than for another, insert an extra stage only for admins, or enable self-registration (enrollment) for only certain applications. It&rsquo;s flexible in the good sense of the word: you&rsquo;re not stuck with the default design if you need something else.</p>
<h3 id="why-it-matters">Why it matters</h3>
<p>The reason a centralized IdP is worth the investment is simple: every app you add without SSO is a new attack surface with its own authentication mechanism, usually weaker than you&rsquo;d like (no MFA, no password-complexity policy, no centralized logging). When you centralize identity, you enforce a single MFA policy everywhere, you get one place to see who logged in where and when, and one switch to flip when someone leaves the team, instead of walking through ten apps closing accounts one by one.</p>
<p>To be upfront about it: DreamServer&rsquo;s own internal SSO runs on <a href="https://www.authelia.com/" rel="nofollow">Authelia</a>
, not authentik, mostly because Authelia&rsquo;s lightweight single-binary/config-file model fit our stack better. That doesn&rsquo;t make authentik the wrong choice, it&rsquo;s a genuinely strong, more full-featured identity provider, especially if you need SAML, SCIM, RADIUS, or self-service enrollment flows that Authelia doesn&rsquo;t attempt to cover. These are two tools built with different philosophies, not a winner and a runner-up.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The feature set in authentik goes well beyond &ldquo;log in once, access everything&rdquo;:</p>
<ul>
<li><strong>Real multi-protocol support</strong>: SAML and OIDC for modern web apps, LDAP for services expecting a classic directory (plenty of legacy apps, some NAS boxes, some enterprise tools), SCIM for automated account provisioning and de-provisioning in third-party services, and RADIUS for network-level or VPN authentication. You can effectively replace four different auth systems with one.</li>
<li><strong>The flows-and-stages engine</strong>: login, enrollment (self-registration), password recovery, and other identity workflows are configurable flows, not fixed screens. You can add, remove, or reorder stages without writing code.</li>
<li><strong>A forward-auth (proxy) provider</strong>: for applications that have no native authentication of their own, authentik can sit in front of them as a forward-auth provider and gate access, conceptually similar to what Authelia or oauth2-proxy do. Useful for internal tools, dashboards, or utilities with no login system of their own.</li>
<li><strong>Solid MFA</strong>: TOTP (Google Authenticator and equivalents) plus WebAuthn/passkeys, so you can enforce passwordless or hardware-key authentication wherever you need that level of security.</li>
<li><strong>An admin UI</strong>: you manage applications, providers, users, groups, and flows from a web interface instead of hand-editing configuration files for every small change.</li>
</ul>
<p>Under the hood, the backend is built on Python/Django plus a Go component, and it&rsquo;s typically self-hosted via Docker/docker compose or Kubernetes.</p>
<h2 id="installing-authentik-on-a-vps">Installing authentik on a VPS</h2>
<p>A <a href="/en/virtual-servers/vps/">VPS</a>
 is a reasonable place to run authentik: you get full control over the network and the firewall, which matters a lot when you&rsquo;re putting into production an identity system that everything else now depends on. The official install path uses docker compose, with a server service, a worker service (for asynchronous tasks like sending emails or processing more complex flows), plus PostgreSQL and Redis as dependencies:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p /opt/authentik <span style="color:#f92672">&amp;&amp;</span> cd /opt/authentik
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># fetch the official compose file and generate secrets into a .env file</span>
</span></span><span style="display:flex;"><span>curl -o docker-compose.yml https://goauthentik.io/docker-compose.yml
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;PG_PASS=</span><span style="color:#66d9ef">$(</span>openssl rand -base64 <span style="color:#ae81ff">36</span> | tr -d <span style="color:#e6db74">&#39;\n&#39;</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> &gt;&gt; .env
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;AUTHENTIK_SECRET_KEY=</span><span style="color:#66d9ef">$(</span>openssl rand -base64 <span style="color:#ae81ff">60</span> | tr -d <span style="color:#e6db74">&#39;\n&#39;</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> &gt;&gt; .env
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># bring the stack up (server + worker + postgres + redis)</span>
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span></code></pre></div><p>The basic shape is simple enough: the server container serves the UI and the API, the worker processes background tasks, and Postgres and Redis hold state and the job queue. This is not a single binary, it&rsquo;s a small stack of services that need to run together, and that&rsquo;s worth factoring into your resource sizing and your operational expectations.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># allow only the HTTPS/HTTP traffic needed to reach authentik</span>
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># during initial setup, consider restricting access to the admin</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># interface to your own IPs only (an extra rule on the admin path,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># or a reverse proxy allowlist), then relax it once MFA is enabled</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># on the admin account</span>
</span></span></code></pre></div><p>If you&rsquo;d rather not deal with the OS, firewall, and server maintenance side of things yourself, <a href="/en/services/server-administration/">server administration</a>
 is exactly for this kind of work: you handle authentik&rsquo;s configuration, we handle the rest.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="design-your-first-login-flow-instead-of-accepting-the-defaults-blindly">Design your first login flow instead of accepting the defaults blindly</h3>
<p>It&rsquo;s tempting to leave the default login flow exactly as it ships, but it&rsquo;s worth stopping to think about which stages you actually want: identification by email or username, password verification, then mandatory MFA for everyone or just for a specific group (admins, say). Because every stage is a separate object you can reorder or condition, you can build a different flow for internal users versus external or test accounts, without duplicating any logic.</p>
<h3 id="wire-the-forward-auth-provider-in-front-of-apps-with-no-native-login">Wire the forward-auth provider in front of apps with no native login</h3>
<p>Plenty of internal tools (dashboards, monitoring utilities, status pages) have no authentication mechanism at all, they simply assume that whoever reaches them is allowed to be there. authentik&rsquo;s forward-auth provider solves exactly that: you point your reverse proxy to check the session against authentik before letting the request through, and the app behind it doesn&rsquo;t even need to know an auth layer exists in front of it.</p>
<h3 id="choose-oidc-saml-or-ldap-based-on-what-the-app-actually-supports-not-your-preference">Choose OIDC, SAML, or LDAP based on what the app actually supports, not your preference</h3>
<p>There is no universally &ldquo;best&rdquo; protocol, only what the target application supports. Modern, web-native apps almost always speak OIDC, less often SAML (common in enterprise or education environments). Older services or network gear often only speak LDAP. Check the target app&rsquo;s documentation before you configure a new provider in authentik, not the other way around.</p>
<h3 id="enforce-mfa-policy-from-one-place-not-per-application">Enforce MFA policy from one place, not per application</h3>
<p>One of the biggest wins of a centralized IdP is that you can enforce MFA (TOTP or WebAuthn/passkeys) at the login flow level, once, and that policy automatically applies to every application using authentik as its provider. No more configuring MFA separately in each app, with the risk of missing one and leaving a door open.</p>
<h2 id="where-authentik-is-not-the-right-answer">Where authentik is not the right answer</h2>
<p>Let&rsquo;s be direct: authentik is a heavier stack than it might look at first glance. Django plus a worker plus PostgreSQL plus Redis means four pieces that need to run together, get monitored, and get updated, compared to a config-file-plus-single-binary tool. If all you need is a simple forward-auth gate in front of a handful of self-hosted apps, no SAML, no SCIM, no RADIUS, the extra complexity probably isn&rsquo;t worth the operational cost.</p>
<p>The flexibility of the flows-and-stages engine comes with its own price: the learning curve is not trivial. This isn&rsquo;t a &ldquo;configure it in five minutes and forget about it&rdquo; system; you need to understand how stages compose and how policies interact before you&rsquo;ll feel comfortable changing anything in production.</p>
<p>And if you already have a small homelab with a handful of apps and no real multi-protocol requirements, a simpler tool can get you 90% of the value with far less operational overhead. Not everything worth doing is worth doing with the most feature-complete tool available.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><a href="https://github.com/authelia/authelia" rel="nofollow">Authelia</a>
 is the most direct alternative: lighter, a single binary, configuration driven by a YAML file, built mainly for forward-auth SSO in front of a set of self-hosted apps. As mentioned above, it&rsquo;s exactly what DreamServer runs internally, precisely for the operational simplicity. It doesn&rsquo;t have authentik&rsquo;s protocol ambitions (no native SAML, no SCIM, no RADIUS), but for a lot of use cases it covers exactly what you need without the extra ballast.</p>
<p><a href="https://github.com/keycloak/keycloak" rel="nofollow">Keycloak</a>
 sits at the opposite end in terms of enterprise maturity: a Java-based identity provider with protocol coverage as broad as authentik&rsquo;s, but its own heavier JVM footprint and a different, more classically enterprise administration model. If you&rsquo;re already deep in a Java-heavy ecosystem, or you need specific enterprise integrations, it&rsquo;s worth a look.</p>
<p><a href="https://github.com/ory" rel="nofollow">Ory</a>
 is worth mentioning for a different angle entirely: a componentized identity stack built more for developers and APIs first, where you pick the pieces you need (Kratos for identity, Hydra for OAuth2/OIDC, and so on) instead of a single monolith with an admin UI. It makes sense if you&rsquo;re building your own product and want fine-grained control from code, less so if you want something ready to administer through a UI from day one.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you need broad protocol coverage (SAML for an enterprise partner, LDAP for an aging NAS, RADIUS for VPN, OIDC for everything else), or you want configurable self-registration and password-recovery flows, or you have a pile of heterogeneous apps you want to put behind a single forward-auth gate, authentik is a solid, mature choice. If instead you want something easier to operate, with fewer moving parts, and protocol breadth isn&rsquo;t a real requirement for you, consider Authelia before jumping into Django plus worker plus Postgres plus Redis.</p>
<p>Whichever you pick, a <a href="/en/virtual-servers/vps/">VPS</a>
 with dedicated resources and full network control is the right place to run it, and if you&rsquo;d rather not handle the system side yourself, <a href="/en/services/server-administration/">server administration</a>
 is there for exactly that.</p>
]]></content:encoded><category>Security</category><category>SSO</category></item><item><title>mkcert: local HTTPS with no browser warnings, in two commands</title><link>https://dreamserver.ro/en/blog/mkcert-locally-trusted-dev-certificates/</link><pubDate>Sun, 14 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/mkcert-locally-trusted-dev-certificates/</guid><description>A hands-on look at mkcert, the tool that generates locally-trusted development certificates: how it installs a local CA into the trust store, how you issue a cert for any domain and IP, plus mkcert Web UI for teams. No self-signed, no browser warnings.</description><content:encoded><![CDATA[<p>Everyone has been through it: you start a local service on <code>https://localhost</code>, and the browser greets you with a red &ldquo;your connection is not private&rdquo; screen. You click &ldquo;proceed anyway&rdquo;, keep working, and get used to the warning. The problem is that this warning trains you to ignore exactly the signal that should stop you in production, plus some tools (webhooks, mobile apps, service workers) simply refuse to talk to a certificate they do not trust. Self-signed certificates are not the answer, they are the source of the pain.</p>
<p><a href="https://github.com/FiloSottile/mkcert" rel="nofollow">mkcert</a>
, the tool written by <a href="https://filippo.io/" rel="nofollow">Filippo Valsorda</a>
 (former Go security lead at Google), solves this elegantly. It is licensed under BSD-3, it is one of the most beloved dev tools in the ecosystem, and we use it in our own workflows. This post is the field report, plus the web-interface variant we run for teams.</p>
<h2 id="what-mkcert-actually-is">What mkcert actually is</h2>
<p>mkcert does one thing and does it perfectly: <strong>it generates locally-trusted development certificates</strong>. Not self-signed certificates the browser rejects, but certificates signed by your own <strong>certificate authority (CA)</strong> that mkcert installs into your system and browser trust stores. From that moment, any certificate issued by that CA is considered valid by your machine, with no warning at all.</p>
<p>The subtle but crucial difference from self-signed is trust. A self-signed certificate has nobody behind it vouching for it, so the browser complains. An mkcert certificate is signed by your local CA, and your local CA is explicitly trusted on your machine because mkcert registered it there. It is the same mechanism by which Let&rsquo;s Encrypt or any public CA works, just at the scale of your machine, not the internet.</p>
<h3 id="how-it-works-underneath">How it works underneath</h3>
<p>The flow has two steps, and that is all you need to understand:</p>
<ol>
<li><strong><code>mkcert -install</code></strong>: creates a CA key pair (once, on the first run) and installs it into the relevant trust stores, the operating system, plus Firefox and Chrome via the NSS database. From this moment, your machine trusts anything this CA signs.</li>
<li><strong><code>mkcert example.test localhost 127.0.0.1 ::1</code></strong>: issues a certificate valid for exactly those names and IPs, signed by your local CA. It writes two files, the certificate and the private key, which you hand to your local server.</li>
</ol>
<p>You put the certificate into nginx, into your Node app, into anything, and <code>https://example.test</code> works with the green padlock, without a single warning. Two steps, zero configuration, zero PKI theory to learn.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>mkcert is minimalist by design, but it covers everything you need:</p>
<ul>
<li><strong>Multiple names on one certificate</strong>: domains, subdomains, <code>localhost</code>, IP addresses (IPv4 and IPv6), all in a single certificate.</li>
<li><strong>Wildcard certificates</strong>: <code>mkcert &quot;*.example.test&quot;</code> covers all subdomains at once.</li>
<li><strong>Cross-platform</strong>: macOS, Linux and Windows, installable via Homebrew, distro packages, Chocolatey/Scoop or a direct binary.</li>
<li><strong>Multiple trust stores</strong>: system, Firefox, Chrome, Java, NSS, all updated by <code>-install</code>.</li>
<li><strong>Formats</strong>: PEM by default, plus PKCS#12 (<code>.p12</code>) for tools that require it.</li>
<li><strong>S/MIME certificates</strong>: for email signing in development.</li>
</ul>
<p>What it does not do, by design: <strong>mkcert is not for production</strong>. Its CA is only trusted on the machines where you manually installed it. A random visitor from the internet will see the same red screen, because their browser does not know your CA. For public certificates you need a public CA (Let&rsquo;s Encrypt, Google Trust Services), and mkcert does not even try to compete there. It is the tool for development, labs and internal networks, exactly where it shines.</p>
<h2 id="installing-and-your-first-certificate">Installing and your first certificate</h2>
<p>On Linux, the simplest way is through the package manager or the direct binary:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Debian/Ubuntu</span>
</span></span><span style="display:flex;"><span>sudo apt install libnss3-tools
</span></span><span style="display:flex;"><span>curl -JLO <span style="color:#e6db74">&#34;https://dl.filippo.io/mkcert/latest?for=linux/amd64&#34;</span>
</span></span><span style="display:flex;"><span>chmod +x mkcert-v*-linux-amd64
</span></span><span style="display:flex;"><span>sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># install the local CA (once)</span>
</span></span><span style="display:flex;"><span>mkcert -install
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># issue a certificate for your project</span>
</span></span><span style="display:flex;"><span>mkcert example.test <span style="color:#e6db74">&#34;*.example.test&#34;</span> localhost 127.0.0.1 ::1
</span></span></code></pre></div><p>The second command writes <code>example.test+4.pem</code> (the certificate) and <code>example.test+4-key.pem</code> (the key). You point your server config at them and you are done. In nginx, for example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">server_name</span> <span style="color:#e6db74">example.test</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ssl_certificate</span>     <span style="color:#e6db74">/path/example.test+4.pem</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/path/example.test+4-key.pem</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>libnss3-tools</code> matters on Linux: without it, mkcert cannot install the CA into Firefox and Chrome, only into the system trust store. With it, all browsers trust it from the first go.</p>
<h2 id="mkcert-for-a-team-the-web-interface">mkcert for a team: the web interface</h2>
<p>mkcert on the command line is perfect for a single developer on their machine. But when you have a team or an internal lab where several people need certificates for internal services, distributing keys by hand over Slack gets ugly fast. This is where <a href="https://github.com/jeffcaldwellca/mkcertWeb" rel="nofollow">mkcert Web UI</a>
 comes in, a web layer over mkcert that we run for exactly this need.</p>
<p>It gives you a dashboard where you issue certificates for any domain and IP without touching the terminal, in PEM, CRT or PFX formats. It also brings things useful for a team:</p>
<ul>
<li><strong>Expiry monitoring</strong> with email notifications before a certificate expires.</li>
<li><strong>SCEP</strong>, the automatic device enrollment protocol.</li>
<li><strong>Authentication</strong>, basic auth or SSO through OpenID Connect.</li>
<li><strong>Dark/light theme</strong> and a clean UI.</li>
</ul>
<p>We keep it behind our SSO, so only our people can issue certificates from the internal CA. One hygiene step: <strong>the CA is a trust key, treat it as a secret</strong>. Anyone with the CA&rsquo;s private key can issue certificates your machines will believe. Do not expose it, do not put it in Git, and restrict who reaches the interface.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few things useful beyond the defaults.</p>
<h3 id="distribute-the-ca-not-the-certificates">Distribute the CA, not the certificates</h3>
<p>If you have a team, do not issue and distribute certificates one by one. Distribute the <strong>public CA</strong> once (<code>mkcert -CAROOT</code> shows you where it is), install it on the team&rsquo;s machines, and from there each person issues whatever certificates they want, all trusted on all machines. One CA, many certificates.</p>
<h3 id="use-a-test-or-localhost-domain">Use a <code>.test</code> or <code>.localhost</code> domain</h3>
<p>For development, use reserved TLDs like <code>.test</code> or <code>.localhost</code>, which will never exist publicly. You avoid collisions with real domains and it is clear from the name that it is a development environment.</p>
<h3 id="do-not-bake-the-ca-into-public-docker-images">Do not bake the CA into public Docker images</h3>
<p>The temptation is to put the CA into a development image so it is &ldquo;ready&rdquo;. Do not do it for images that end up in a public registry, you have published a CA key. Mount it at runtime from a volume, do not bake it into the image.</p>
<h3 id="pkcs12-for-stubborn-tools">PKCS#12 for stubborn tools</h3>
<p>Some tools (Java, certain clients) do not want PEM but <code>.p12</code>. mkcert outputs the format directly with <code>mkcert -pkcs12 example.test</code>, without wrestling with openssl incantations.</p>
<h2 id="where-mkcert-is-not-the-right-answer">Where mkcert is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not for production.</strong> We repeat it because it is the most common mistake: the mkcert CA is only trusted where you manually installed it. A public site needs a certificate from a public CA.</li>
<li><strong>Not certificate management at scale.</strong> For automatically rotating hundreds of production certificates you need ACME (Let&rsquo;s Encrypt, Google Trust Services), not mkcert.</li>
<li><strong>The local CA is a risk surface.</strong> If someone steals your CA key and installs it without your knowledge, they can issue certificates you trust. Treat <code>-CAROOT</code> with care.</li>
<li><strong>Does not solve trust between foreign machines.</strong> Two computers that do not have the same CA installed will not trust each other&rsquo;s certificates. It is a local-first tool by definition.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at plain openssl (you can do everything mkcert does, but with ten cryptic commands and good odds of getting a field wrong), <a href="https://caddyserver.com/" rel="nofollow">Caddy</a>
 (which automatically issues locally-trusted certificates for development, excellent if you use it as your server anyway), and <a href="https://smallstep.com/docs/step-ca/" rel="nofollow">step-ca</a>
 from Smallstep (a full private CA with ACME, far more capable but also far heavier, right when you actually want a serious internal PKI). For &ldquo;I want local HTTPS that works now, without becoming a PKI expert&rdquo;, mkcert is the most on-target.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you develop any local web service and you are tired of the red screen, mkcert is worth the five minutes to install. The first time <code>https://your-project.test</code> opens with the green padlock, without a single warning, without having learned a word of PKI theory, you will understand why it is one of the most beloved dev tools in the ecosystem.</p>
<p>If you have a team or an internal lab, add mkcert Web UI behind an SSO and distribute the CA once to all machines, and issuing certificates becomes a click. We run it exactly like that, for our internal services. If you want to put it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 are perfect for a small internal certificate server, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the setup and SSO integration if you would rather skip the learning curve.</p>
<p>Either way, an evening with no red browser screens is a better evening. The code is on GitHub, the install is one command, and you will know within five minutes whether it earns a spot in your workflow.</p>
]]></content:encoded><category>Security</category><category>TLS</category></item><item><title>Grafana: the single pane of glass for your whole infrastructure</title><link>https://dreamserver.ro/en/blog/grafana-open-source-monitoring-dashboards/</link><pubDate>Wed, 10 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/grafana-open-source-monitoring-dashboards/</guid><description>A hands-on look at Grafana, the open-source visualization layer for monitoring: how it pairs with a metrics backend like VictoriaMetrics or Prometheus, dashboards-as-code, alerting, install on a VPS in minutes, and the limits to know before production.</description><content:encoded><![CDATA[<p>A server is dragging at 3 AM. Which one, and why? With nowhere to look, the answer is a frantic <code>ssh</code> across five machines, a <code>top</code> that shows you one second of the system&rsquo;s life, and a vague feeling that &ldquo;it was fine yesterday&rdquo;. With a wall of dashboards in front of you, the answer is a glance away: node 3 climbed on I/O forty minutes ago, exactly when a backup kicked off, and the CPU is actually normal. That is the difference Grafana makes.</p>
<p><a href="https://github.com/grafana/grafana" rel="nofollow">Grafana</a>
, the open-source visualization layer from <a href="https://grafana.com/" rel="nofollow">Grafana Labs</a>
, is effectively the de facto standard for the &ldquo;single pane of glass&rdquo; that infrastructure teams stare at. As of version 13.0 (released April 2026) it is licensed under AGPLv3, has tens of thousands of stars on GitHub, and runs on anything from a home Raspberry Pi to fleets of thousands of servers. We have been running it in front of our own infrastructure, in our Bucharest datacenter, for years. This post is the field report.</p>
<h2 id="what-grafana-actually-is">What Grafana actually is</h2>
<p>First confusion to clear up: <strong>Grafana is not a complete monitoring system, it is the visualization and alerting layer on top of one</strong>. It does not collect metrics itself, it does not store time series, and it has no agent crawling your servers. Grafana connects to a <strong>data source</strong> that already holds the numbers, queries it, and draws it into dashboards.</p>
<p>That is the single most important thing to understand before anything else: Grafana needs a <strong>data backend</strong> behind it. The most common pairing is Grafana plus <a href="https://prometheus.io/" rel="nofollow">Prometheus</a>
 or, as we run it, <a href="https://victoriametrics.com/" rel="nofollow">VictoriaMetrics</a>
 (a drop-in Prometheus-compatible store that is several times more disk-efficient). Prometheus/VictoriaMetrics scrape metrics from exporters (<code>node_exporter</code> for hosts, <code>cadvisor</code> for containers, SNMP exporters for switches), store them, and Grafana reads them back through PromQL and turns them into graphs.</p>
<p>Architecturally, Grafana is a service written in Go with a TypeScript/React frontend, and it keeps its own configuration (dashboards, users, data sources, alert rules) in a database (SQLite by default, or PostgreSQL/MySQL for large setups). Do not confuse that database with your monitoring data: the Grafana database only holds metadata, the time series live in the data source.</p>
<h3 id="why-the-data-source-model-matters">Why the data-source model matters</h3>
<p>Grafana&rsquo;s real power is that it <strong>decouples visualization from storage</strong>. The same dashboard can combine, on one screen, panels that pull from three different sources: metrics from VictoriaMetrics, logs from <a href="https://grafana.com/oss/loki/" rel="nofollow">Loki</a>
, and a direct query into an application PostgreSQL. You are not locked to a single data vendor. When someone tells you &ldquo;the app was slow at 14:32&rdquo;, you put a latency panel next to an error-log panel next to a CPU panel, all lined up on the same time axis, and you see the correlation at a glance.</p>
<p>Grafana supports dozens of native data sources: Prometheus, VictoriaMetrics, Loki, Tempo (traces), InfluxDB, Graphite, Elasticsearch, PostgreSQL, MySQL, CloudWatch and many more via plugins. In practice you pick the metrics backend by scale, and Grafana stays constant on top.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The open-source edition (Grafana OSS, which is what we are talking about; there is also a commercial Enterprise edition, more on it below) ships with everything you need for serious observability:</p>
<ul>
<li><strong>Dashboards and panels</strong> of every kind: time-series, gauge, stat, table, heatmap, bar chart, state timeline. Each panel is a query plus a visual representation.</li>
<li><strong>Unified alerting</strong>: alert rules that evaluate a query on an interval and fire when a threshold is crossed, with contact points (email, Slack, Telegram, webhook, PagerDuty) and notification policies that route the alerts.</li>
<li><strong>Explore</strong>, an ad-hoc query mode for when you want to dig into PromQL or LogQL without building a dashboard, exactly what you need in the middle of an incident.</li>
<li><strong>Provisioning as-code</strong>: data sources, dashboards and alert rules can be defined in YAML/JSON files version-controlled in Git, not just clicked into the UI (more on this below, it is the single best thing to do).</li>
<li><strong>Variables and templating</strong>: one dashboard with a <code>$host</code> dropdown that shows any server in the fleet, instead of one dashboard per machine.</li>
<li><strong>Transformations</strong>: joins, calculations and filters on query results, right in the panel, without touching the data source.</li>
<li><strong>Flexible authentication</strong>: native login, OAuth (Google, GitHub, generic OIDC), LDAP, or reverse-proxy auth (how we do it, with a header injected by an SSO layer in front).</li>
<li><strong>Annotations</strong>: markers on the time axis for deploys, incidents or maintenance windows, so you immediately see &ldquo;aha, the graph jumped exactly when we shipped&rdquo;.</li>
</ul>
<p>What it does not do: Grafana does not collect metrics (that is the exporters plus Prometheus/VictoriaMetrics), does not aggregate logs itself (that is Loki or Elasticsearch), and does not replace an ITIL system with ticketing and acknowledgement. It is the visualization and alerting layer, and it is the best at it.</p>
<h2 id="installing-grafana-on-a-vps">Installing Grafana on a VPS</h2>
<p>Grafana is a single Go binary, so installing it is easy. The cleanest way on a modern server is via Docker, alongside a metrics backend. A minimal <code>docker compose</code> example, Grafana plus VictoriaMetrics:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">victoriametrics</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">victoriametrics/victoria-metrics:latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">command</span>: [<span style="color:#e6db74">&#34;--retentionPeriod=12&#34;</span>, <span style="color:#e6db74">&#34;--storageDataPath=/vmdata&#34;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./vmdata:/vmdata</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">grafana</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">grafana/grafana-oss:13.0.0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;3000:3000&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./grafana:/var/lib/grafana</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./provisioning:/etc/grafana/provisioning</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">GF_SECURITY_ADMIN_PASSWORD</span>: <span style="color:#e6db74">&#34;change-me&#34;</span>
</span></span></code></pre></div><p>Bring the stack up with <code>docker compose up -d</code>, then the UI is at <code>http://&lt;your-server-ip&gt;:3000/</code> with user <code>admin</code> and the password you set. The first thing you do: wire up the data source. Not by clicking, but through provisioning (see below) so it is reproducible.</p>
<p>As with any management interface, <strong>port 3000 should not be exposed to the whole world without a protection layer</strong>. Either put it behind a reverse proxy with TLS and authentication, or restrict it to your IPs with a firewall rule:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow from YOUR_OFFICE_IP to any port <span style="color:#ae81ff">3000</span> proto tcp
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">3000</span>
</span></span></code></pre></div><p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, put the same rule in your provider&rsquo;s network firewall too, do not rely on host-level rules alone for management interfaces.</p>
<h2 id="dashboards-as-code-the-single-best-thing-you-can-do">Dashboards-as-code: the single best thing you can do</h2>
<p>The temptation at first is to build everything in the UI: add the data source with clicks, make dashboards by hand, set alerts from the interface. It works, but you have built a black box you cannot recreate if the server dies. The clean solution is <strong>provisioning</strong>, where everything about the configuration lives in files version-controlled in Git.</p>
<p>The data source, a YAML file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># /etc/grafana/provisioning/datasources/vm.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">datasources</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">VictoriaMetrics</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#ae81ff">prometheus</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">access</span>: <span style="color:#ae81ff">proxy</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">url</span>: <span style="color:#ae81ff">http://victoriametrics:8428</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">isDefault</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>A dashboard provider that auto-loads any JSON from a folder:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># /etc/grafana/provisioning/dashboards/default.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">providers</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">DreamServer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">folder</span>: <span style="color:#ae81ff">DreamServer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#ae81ff">file</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">options</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/var/lib/grafana/dashboards</span>
</span></span></code></pre></div><p>And the dashboards themselves are JSON files in that folder. You do not have to write them from scratch: the Grafana community has thousands of ready-made dashboards at <a href="https://grafana.com/grafana/dashboards/" rel="nofollow">grafana.com/dashboards</a>
. A few we use and recommend:</p>
<ul>
<li><strong>Node Exporter Full</strong> (ID <code>1860</code>): everything you could want about a Linux host, CPU/RAM/disk/network, at an absurd level of detail.</li>
<li><strong>Ceph Cluster</strong>: the health of a Ceph storage cluster.</li>
<li>A container dashboard via <strong>cAdvisor</strong>, so you see which container is eating the CPU, not just that the host is loaded.</li>
</ul>
<p>You import them once, save them as JSON in the provisioned folder, and from there they are in Git. On the next deploy, a fresh Grafana comes up with all the dashboards already there. That is the difference between &ldquo;I have a Grafana&rdquo; and &ldquo;I have an observability platform I can rebuild in a minute&rdquo;.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few things we found genuinely useful beyond the defaults.</p>
<h3 id="reverse-proxy-authentication">Reverse-proxy authentication</h3>
<p>Grafana&rsquo;s native login is fine for a single person, but if you already run company SSO, disable it and let a reverse proxy do the authentication, injecting the user into a header:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[auth.proxy]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">enabled</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">true</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">header_name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">X-WEBAUTH-USER</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">auto_sign_up</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">true</span>
</span></span></code></pre></div><p>That way people log in once, through the company identity, and Grafana creates them automatically on first access. When someone leaves, you disable them in the SSO and their access to the dashboards vanishes.</p>
<h3 id="one-dashboard-with-variables-not-fifty-dashboards">One dashboard with variables, not fifty dashboards</h3>
<p>Do not build one dashboard per server. Build a single one with a <code>$host</code> variable populated from a query (<code>label_values(node_uname_info, instance)</code>) and a dropdown that swaps the whole page between machines. Add a new server to the fleet and it shows up in the dropdown on its own. Less to maintain, easier to compare two nodes.</p>
<h3 id="alert-on-symptoms-not-causes">Alert on symptoms, not causes</h3>
<p>The temptation is to put an alert on every metric. The result is noise and alerts you learn to ignore. Alert on <strong>what hurts the user</strong>: a disk nearly full, a service that stopped responding, latency that jumped, an error rate that is climbing. CPU at 90% is not a problem if the app responds fine. Keep the number of alerts small enough that you wake up for every one.</p>
<h3 id="send-alerts-somewhere-you-actually-see-them">Send alerts somewhere you actually see them</h3>
<p>An email contact point is the minimum; a dedicated Slack/Telegram channel is better, because alerts do not drown in an inbox. Configure <code>failure</code> with a threshold of a few consecutive evaluations so it does not scream at the first transient spike, and enable the resolved notification so you know when it settled on its own.</p>
<h3 id="read-explore-mode-before-building-panels">Read Explore mode before building panels</h3>
<p>When you investigate something new, do not build a dashboard. Open <strong>Explore</strong>, write ad-hoc PromQL queries, see which metrics exist and what they look like. Only after you have found the query that says something do you put it in a panel. Explore is where you understand the data; the dashboard is where you present it.</p>
<h2 id="where-grafana-is-not-the-right-answer">Where Grafana is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not a database.</strong> Without a metrics backend (Prometheus, VictoriaMetrics, InfluxDB) there is nothing to visualize. Grafana alone monitors nothing; it is the top half of the stack.</li>
<li><strong>Does not collect logs itself.</strong> For logs you need Loki or Elasticsearch behind it. Grafana draws them, but something else gathers them.</li>
<li><strong>Not a replacement for a classic auto-discovery monitoring system.</strong> Tools like Checkmk or Zabbix discover services on their own, keep hardware/software inventory, and have an ITIL acknowledgement workflow. Grafana is complementary: graphs and correlation, not ticketing. We run it in parallel with Checkmk without them stepping on each other.</li>
<li><strong>AGPLv3 matters if you modify and offer it as a service.</strong> For internal use, the license does not touch you. But if you modify Grafana and offer it as a network service, you are obligated to publish your changes. For the vast majority of cases, irrelevant.</li>
<li><strong>Enterprise features are closed.</strong> Scheduled PDF reports, granular RBAC, LDAP team sync and a few enterprise data-source plugins (Splunk, Oracle, ServiceNow) are in the commercial edition. The OSS edition is generous, but if you need reports emailed automatically to management, plan accordingly.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at <a href="https://www.elastic.co/kibana" rel="nofollow">Kibana</a>
 (excellent if you live in the Elasticsearch ecosystem, weaker outside it), <a href="https://www.netdata.cloud/" rel="nofollow">Netdata</a>
 (sensational for instant per-node visibility with zero configuration, but less suited as a centralized fleet pane), <a href="https://www.zabbix.com/" rel="nofollow">Zabbix</a>
 and Checkmk (complete monitoring systems with auto-discovery and alerting, but with more rigid graphing), and <a href="https://perses.dev/" rel="nofollow">Perses</a>
 (a newer, fully open-source, dashboard-as-code-first project, worth watching).</p>
<p>Grafana ended up being the cleanest fit for &ldquo;I want a single pane over which I can put any data source and correlate everything, without locking into a vendor&rdquo;. Your weights might be different.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you manage more than a few servers and you are tired of <code>ssh</code>-ing into each one when something goes wrong, Grafana plus a metrics backend (VictoriaMetrics is our pick) is worth a Sunday afternoon. The install is fast, community dashboards give you value in five minutes, and the first time a graph shows you the cause of an incident before you even manage to panic, you will know it has earned a permanent place in your stack.</p>
<p>If you run it in production, keep the configuration in Git through provisioning, put it behind SSO, and treat its database (SQLite or Postgres) as infrastructure with backups. Dashboards rebuild from JSON in a minute; what you would actually lose are the users, the alerts and the annotation history.</p>
<p>We run Grafana over VictoriaMetrics for our entire fleet, with dashboards provisioned from Git and alerts on email, and we recommend it to any customer who asks how to see their infrastructure. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 start at small enough sizes to make the experiment cheap, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install, the metrics backend and tuning if you would rather skip the learning curve.</p>
<p>Either way, putting a dashboard in front of yourself is a better way to spend an evening than another <code>top</code> on another server. The code is on GitHub, a docker compose stack has you running in five minutes, and you will know within an hour whether it earns a spot in front of your infrastructure.</p>
]]></content:encoded><category>Monitoring</category><category>Observability</category></item><item><title>Wazuh, Open-Source XDR and SIEM for When Grep Isn't a Security Strategy Anymore</title><link>https://dreamserver.ro/en/blog/wazuh-open-source-xdr-siem/</link><pubDate>Sun, 07 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/wazuh-open-source-xdr-siem/</guid><description>Wazuh combines XDR and SIEM in one open-source platform: agents, FIM, intrusion detection, vulnerability scanning and compliance. When it's worth running.</description><content:encoded><![CDATA[<p>You&rsquo;ve got a handful of servers, maybe a VPS or two running something that matters, and when something looks off you SSH in and grep the logs for a suspicious IP or an error message that keeps repeating. That works fine as long as you&rsquo;re the only one watching and the machine count fits on one hand. Then infrastructure grows: more servers, maybe a client asking for a security audit or a compliance requirement (PCI DSS, GDPR, HIPAA) that wants actual proof you&rsquo;re monitoring file integrity, detecting intrusions, and keeping centrally analyzed logs, not just scattered text files on each box. At that point, manual grepping stops being a security strategy and becomes luck that hasn&rsquo;t run out yet.</p>
<p>Wazuh (repo at <a href="https://github.com/wazuh/wazuh" rel="nofollow">github.com/wazuh/wazuh</a>
, official site <a href="https://wazuh.com" rel="nofollow">wazuh.com</a>
) is built for exactly that jump: an open-source platform, licensed under GPLv2, that combines XDR (Extended Detection and Response) with SIEM (Security Information and Event Management) in a single stack. For organizations that need centralized log analysis and file integrity monitoring across a fleet of servers, Wazuh is one of the strongest open-source options available today, not because it&rsquo;s the only one, but because it realistically covers the whole cycle: telemetry collection, detection, correlation against known frameworks, alerting, and compliance reporting.</p>
<h2 id="what-wazuh-actually-is">What Wazuh actually is</h2>
<p>At its core, Wazuh works with lightweight agents installed on endpoints and servers, which collect telemetry (system and application logs, system calls, file changes, software inventory) and forward it to a central Wazuh server. There, the data gets decoded, matched against a ruleset, and turned into meaningful alerts instead of raw lines of text. Around that core sits an indexer, built on OpenSearch, which stores and indexes the full data volume, and a dashboard, built on OpenSearch Dashboards, where you search, visualize, and configure alerting.</p>
<h3 id="why-centralizing-security-telemetry-beats-eyeballing-individual-server-logs">Why centralizing security telemetry beats eyeballing individual server logs</h3>
<p>When you have a single server, reading logs by hand is a perfectly reasonable approach. The problem shows up at scale: an attacker moving laterally across a network leaves fragmented traces on multiple machines, traces that look harmless in isolation but make sense only once lined up on a single timeline. Log rotation deletes evidence before you notice it, and an auditor asking for proof that you monitor a configuration file&rsquo;s integrity won&rsquo;t settle for &ldquo;I check every now and then.&rdquo; Centralizing security telemetry gives you one ruleset applied consistently, one timeline of events, and one place where alerts show up, regardless of which of your 20 or 200 machines something happened on.</p>
<h3 id="from-ossec-to-a-full-xdrsiem-stack">From OSSEC to a full XDR/SIEM stack</h3>
<p>Wazuh has roots in the OSSEC lineage, an older tool focused almost entirely on log analysis and rule-based intrusion detection, running as a fairly simple binary alongside an agent. Real security needs at an organization don&rsquo;t stop at &ldquo;alert me when a suspicious word shows up in a log,&rdquo; though: you also need correlation against known vulnerabilities, compliance reporting, a searchable dashboard for the security team, and a way to place each detection in the context of a known attack tactic or technique. That&rsquo;s the need that produced today&rsquo;s stack: Wazuh grew from a log-based detection engine into a full XDR/SIEM platform, with its own indexer and dashboard.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>A base Wazuh install isn&rsquo;t a single feature, it&rsquo;s a bundle of capabilities that, taken together, cover most of an operational security program&rsquo;s requirements:</p>
<ul>
<li><strong>Agents for endpoints and servers</strong>: lightweight processes installed on every monitored machine, collecting logs, system events, and inventory data.</li>
<li><strong>File Integrity Monitoring (FIM)</strong>: tracking changes to content, permissions, ownership, and attributes for the files and directories you flag as important.</li>
<li><strong>Log analysis engine</strong>: decoders and rules that turn raw text from logs into classified, prioritized events and alerts.</li>
<li><strong>Intrusion and rootkit detection</strong>: looking for hidden files, cloaked processes, unregistered network listeners, and inconsistencies in system call responses.</li>
<li><strong>Vulnerability detection</strong>: the software inventory collected by agents is checked against continuously updated CVE databases, so you find out which installed packages have known vulnerabilities.</li>
<li><strong>Configuration assessment and compliance</strong>: periodic scans that compare system settings against hardening guides, mapped to standards like PCI DSS, GDPR, and HIPAA.</li>
<li><strong>MITRE ATT&amp;CK mapping</strong>: relevant detections can be tied to a known attack tactic or technique, giving you immediate context instead of just an alert title.</li>
<li><strong>Indexer and dashboard</strong>: OpenSearch for storage and search, plus a web interface for visualization and alerting configuration.</li>
</ul>
<p>Not every organization needs all of this at once, but Wazuh&rsquo;s real value comes from these pieces working together, rather than as separate tools you have to glue together yourself.</p>
<h2 id="installing-wazuh-on-a-vps">Installing Wazuh on a VPS</h2>
<p>Wazuh isn&rsquo;t a single binary you start and forget. It&rsquo;s a multi-component stack (server, indexer, dashboard) plus agents deployed separately on every monitored machine. The most practical way to stand it up on a VPS is a docker compose setup that starts the three central services in an isolated network:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/wazuh/wazuh-docker.git -b v4.9.0
</span></span><span style="display:flex;"><span>cd wazuh-docker/single-node
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># generate the internal certificates used between components</span>
</span></span><span style="display:flex;"><span>docker compose -f generate-indexer-certs.yml run --rm generator
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># start the stack: wazuh-manager, wazuh-indexer, wazuh-dashboard</span>
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># check that every container is healthy</span>
</span></span><span style="display:flex;"><span>docker compose ps
</span></span></code></pre></div><p>That brings up the central server, the indexer, and the dashboard on the same VPS. The agent, however, gets installed separately, on every machine you want to monitor (including, if you want, the VPS running the server itself), configured to report to the Wazuh server&rsquo;s IP address.</p>
<p>Once the stack is up, you still need to open the right ports in the firewall. The dashboard is served over HTTPS, and the server listens on separate ports for agent registration and ongoing communication:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># access to the web dashboard (HTTPS)</span>
</span></span><span style="display:flex;"><span>sudo ufw allow 443/tcp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># registration of new agents</span>
</span></span><span style="display:flex;"><span>sudo ufw allow 1515/tcp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ongoing agent -&gt; server communication</span>
</span></span><span style="display:flex;"><span>sudo ufw allow 1514/tcp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Wazuh server API (optional, for automation)</span>
</span></span><span style="display:flex;"><span>sudo ufw allow 55000/tcp
</span></span></code></pre></div><p>Because this is a multi-service stack, sizing the VPS matters a lot more than it would for a simple command-line utility: the indexer, being built on OpenSearch, wants real memory, and CPU and disk usage scale directly with the number of agents and the volume of ingested logs. A <a href="/en/virtual-servers/vps/">VPS</a>
 undersized for a few dozen agents quickly becomes the point where the dashboard starts lagging and indexing falls behind.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="tuning-fim-to-watch-the-right-paths-not-the-whole-filesystem">Tuning FIM to watch the right paths, not the whole filesystem</h3>
<p>The initial temptation is to point File Integrity Monitoring at everything: /etc, /usr/bin, /var/www, maybe even all of /home. The result is an alert volume where the real signal (an unexpected change to a critical configuration file) gets buried in normal noise (a deploy rewriting dozens of files every day). It&rsquo;s worth starting narrow: system configuration paths, critical binaries, authentication files, expanding the list gradually as you learn what&rsquo;s normal in your environment.</p>
<h3 id="picking-and-writing-decoders-and-rules-for-your-actual-log-sources">Picking and writing decoders and rules for your actual log sources</h3>
<p>Wazuh ships with a generous set of decoders and rules for common software (nginx, sshd, base system logs), but any internal application or less common stack needs its own decoders to turn logs into useful events. The time invested here is what separates a SIEM that actually tells you something concrete from one that just archives text nobody reads.</p>
<h3 id="using-compliance-mappings-as-a-starting-checklist-not-a-certification">Using compliance mappings as a starting checklist, not a certification</h3>
<p>The mappings to PCI DSS, GDPR, or HIPAA are useful as an initial checklist and as evidence for auditors that you have an active monitoring process, but they don&rsquo;t replace an actual compliance audit performed by someone qualified. Treat them as a solid starting point, not a permanently checked box.</p>
<h3 id="sizing-the-indexer-for-your-real-log-volume-and-retention-needs">Sizing the indexer for your real log volume and retention needs</h3>
<p>OpenSearch, the engine behind the indexer, is the component that feels the growth in agent count and log volume the hardest. The retention period you choose (how many days or months you keep indexed data) multiplied by daily event volume gives you your disk requirement directly, and searches over a poorly sharded, large dataset get slow fast. Think about retention and sharding from day one, not after the indexer starts gasping for air.</p>
<h2 id="where-wazuh-is-not-the-right-answer">Where Wazuh is not the right answer</h2>
<p>Honestly, Wazuh is overkill for a single small VPS or a handful of personal projects. A stack with a server, an indexer, and a dashboard, each with its own memory and CPU requirements, is a lot heavier than a single tool quietly running in the background. If all you have is one machine and you just want to know when someone&rsquo;s trying to brute-force SSH, there are much lighter tools built for exactly that need.</p>
<p>Wazuh&rsquo;s real value only shows up once you have enough hosts and log volume to justify a centralized platform, and an actual security or compliance need that requires proof, not just &ldquo;I check the logs sometimes.&rdquo; On top of that, a SIEM doesn&rsquo;t run itself: it needs ongoing rule tuning and someone who actually looks at the alerts, or it turns into exactly the kind of noise you install and then ignore. A Wazuh instance set up and forgotten is worse than no Wazuh at all, because it gives a false sense of security.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>Wazuh isn&rsquo;t the only option, and the right choice depends a lot on scale and actual need:</p>
<ul>
<li><strong><a href="https://github.com/crowdsecurity/crowdsec" rel="nofollow">CrowdSec</a>
</strong>: a behavior-based IPS, much lighter than Wazuh, well suited to smaller setups where you want automatic detection and blocking of common attacks (brute-force, scanning) without standing up a full analysis and indexing stack.</li>
<li><strong><a href="https://github.com/elastic/elasticsearch" rel="nofollow">Elastic Stack / Elastic Security</a>
</strong>: a DIY SIEM approach, where you build the pieces yourself (ingestion, indexing, rules, visualization) using Elastic&rsquo;s components. More flexibility, but also more integration work.</li>
<li><strong><a href="https://github.com/Graylog2/graylog2-server" rel="nofollow">Graylog</a>
</strong>: a lighter-weight alternative, focused on log management rather than a full security bundle with FIM, vulnerability detection, and compliance mappings included from the start.</li>
</ul>
<p>If the real need is automatically blocking common attacks on a few servers, CrowdSec is probably enough. If you already have a compliance requirement and a serious fleet of servers, Wazuh gives you more out of the box.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>Wazuh is the right tool once you already have real infrastructure to monitor, an actual security or compliance requirement, and the resources (human and server) to keep it tuned and actually watched. It&rsquo;s not the right tool for a hobby VPS or a two-machine personal project, where the weight of the stack doesn&rsquo;t pay for itself.</p>
<p>If you&rsquo;re in the camp where Wazuh genuinely makes sense, the first practical step is a <a href="/en/virtual-servers/vps/">VPS</a>
 properly sized for the indexer and server, not leftover resources scraped together from other services. And because this is a stack that needs continuous tuning, not just an initial install, <a href="/en/services/server-administration/">professional server administration</a>
 is often worth investing in from day one, instead of discovering mid-incident that nobody&rsquo;s looked at the dashboard in three months.</p>
]]></content:encoded><category>Security</category></item><item><title>CrowdSec: Crowdsourced Intrusion Prevention for Everyone Else's Servers Too</title><link>https://dreamserver.ro/en/blog/crowdsec-collaborative-security/</link><pubDate>Wed, 03 Jun 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/crowdsec-collaborative-security/</guid><description>CrowdSec brings collaborative, behavior-based intrusion prevention to your servers: what it does, how to install it, and where it honestly falls short.</description><content:encoded><![CDATA[<p>Any server that gets a public IP address starts taking hits within minutes of coming online: SSH login attempts cycling through the same twenty usernames, requests probing for <code>/wp-login.php</code> and <code>.env</code> files that were never there, path traversal payloads, half-finished TLS handshakes from mass scanners running on rented botnets. None of it is personal, it&rsquo;s the internet&rsquo;s background radiation, and it never stops. For years the standard defense was fail2ban: tail a log file, count the failures, ban the offending IP for a while, repeat forever. It works, and plenty of people still run it happily, but it has a blind spot that gets more obvious every year: every server fights the exact same botnets in total isolation. Your box learns nothing from the fact that the IP hammering your SSH port hit a hundred other servers last week, and the moment a ban expires, all memory of the attacker is gone.</p>
<p>CrowdSec (source on GitHub at <a href="https://github.com/crowdsecurity/crowdsec" rel="nofollow">https://github.com/crowdsecurity/crowdsec</a>
, project site at <a href="https://crowdsec.net" rel="nofollow">https://crowdsec.net</a>
, MIT licensed) is what you get when you take the fail2ban idea and add the one ingredient it always lacked: a crowd. It&rsquo;s a free, open-source intrusion prevention system written in Go that parses your logs, runs behavior-based scenarios against them to spot bruteforce attempts, scanning, and known exploitation patterns, and then, if you opt in, shares what it learns with every other CrowdSec instance on the internet. We run CrowdSec in front of our own services here at DreamServer, paired with Traefik as the reverse proxy and its CrowdSec bouncer, to block malicious IPs at the edge before they reach our applications. It&rsquo;s the kind of component that quietly does its job in the background, exactly what you want from anything sitting in the security path.</p>
<h2 id="what-crowdsec-actually-is">What CrowdSec actually is</h2>
<p>Structurally, CrowdSec splits the job into pieces that talk to each other over a small local API. The <code>crowdsec</code> agent does the log parsing: it tails whatever files you point it at (Nginx access logs, Traefik logs, <code>auth.log</code> for SSH, application logs, anything with a timestamp and an IP in it), normalizes each line through a parser, and feeds the result into detection &ldquo;scenarios.&rdquo; A scenario is a small, declarative rule describing a pattern of bad behavior over time: ten failed SSH logins from one IP in two minutes, a burst of 404s that looks like directory bruteforcing, a request matching a known CVE payload signature. When a scenario fires, it produces a &ldquo;decision,&rdquo; which is really just a statement: ban, or captcha, this IP, for this long, for this reason.</p>
<p>The important detail: CrowdSec itself never blocks anything. The LAPI stores decisions in a database (SQLite by default), and separate &ldquo;bouncers&rdquo; enforce them: a firewall bouncer driving iptables or nftables, an Nginx bouncer, a Traefik bouncer/plugin, a Cloudflare bouncer pushing bans into Cloudflare&rsquo;s rules, and others for different chokepoints. The separation is deliberate: detection and enforcement are different concerns, so you can run detection on a box that has no business touching the firewall, or enforce the same decision at multiple layers at once.</p>
<h3 id="why-crowdsourced-detection-beats-fighting-alone">Why crowdsourced detection beats fighting alone</h3>
<p>What actually makes CrowdSec more than &ldquo;fail2ban with better YAML&rdquo; is the community blocklist. Every participating instance can, if you opt in, report the IPs it confirms as attackers back to a central service, and in return receive a stream of IPs other instances have already flagged. In practice, an address that started hammering someone else&rsquo;s SSH port in Germany this morning can already be blocked on your server in Bucharest before it ever sends you a single packet. That&rsquo;s a fundamentally different posture than fail2ban&rsquo;s local-only model: instead of every server independently rediscovering the same handful of scanning botnets, the discovery happens once, somewhere, and the mitigation propagates. It doesn&rsquo;t replace local detection, your own scenarios still catch things specific to your traffic, but it turns thousands of otherwise-isolated servers into something closer to a shared sensor network, a genuinely different security model, not just a marketing angle on an old idea.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The core install gives you a fairly complete toolkit without much extra work:</p>
<ul>
<li>A log parsing and detection engine (the <code>crowdsec</code> agent) watching multiple log sources at once.</li>
<li>A curated catalog of parsers, scenarios, and &ldquo;collections&rdquo; at the Hub, hub.crowdsec.net, contributed by CrowdSec and the community, covering common stacks like Linux, SSH, Nginx, Traefik, and WordPress.</li>
<li>The Local API (LAPI), the source of truth for decisions and what bouncers query.</li>
<li>An ecosystem of bouncers for different enforcement points: host firewalls via iptables/nftables, Nginx, Traefik, Cloudflare, and more.</li>
<li>The opt-in community blocklist, really the whole point of the project&rsquo;s name.</li>
<li><code>cscli</code>, the command-line tool for installing collections, inspecting decisions, and managing bouncers without hand-editing YAML for every change.</li>
</ul>
<p>There&rsquo;s also an optional cloud Console for visibility across a fleet, but nothing about the engine or the bouncers requires it. It&rsquo;s entirely usable fully self-hosted and offline, which matters if &ldquo;send our security telemetry to a third party&rdquo; is a line you don&rsquo;t want to cross.</p>
<h2 id="installing-crowdsec-on-a-vps">Installing CrowdSec on a VPS</h2>
<p>A minimal Docker setup looks roughly like this: the CrowdSec container reads your logs and runs its own LAPI, and a bouncer container queries that LAPI to know what to block.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Run the CrowdSec engine, mounting the logs it should watch</span>
</span></span><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name crowdsec <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v /var/log:/var/log:ro <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v /etc/crowdsec:/etc/crowdsec <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v crowdsec-db:/var/lib/crowdsec/data <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 8080:8080 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  crowdsecurity/crowdsec
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Install collections matching your stack (SSH + generic Linux)</span>
</span></span><span style="display:flex;"><span>docker exec -it crowdsec cscli collections install crowdsecurity/sshd
</span></span><span style="display:flex;"><span>docker exec -it crowdsec cscli collections install crowdsecurity/linux
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Register a bouncer, then start the firewall bouncer with its key</span>
</span></span><span style="display:flex;"><span>docker exec -it crowdsec cscli bouncers add firewall-bouncer
</span></span><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name crowdsec-firewall-bouncer <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --network host <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --cap-add<span style="color:#f92672">=</span>NET_ADMIN <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e BOUNCER_KEY_FILE<span style="color:#f92672">=</span>/etc/crowdsec/bouncer.key <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v /etc/crowdsec/bouncer.key:/etc/crowdsec/bouncer.key:ro <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  crowdsecurity/cs-firewall-bouncer
</span></span></code></pre></div><p>If you&rsquo;re fronting web traffic with Traefik instead of, or alongside, the host firewall, the Traefik CrowdSec bouncer plugin talks to the same LAPI and rejects requests at the reverse proxy layer, exactly the combination we run in front of our own services.</p>
<p>On the host firewall side, keep it simple: CrowdSec&rsquo;s own bouncer handles IP-level blocking dynamically, so your static rules just need to stay out of the way for legitimate traffic:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># ufw note: keep the baseline permissive for the ports you actually serve,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># and let the CrowdSec bouncer handle dynamic IP blocking on top of it</span>
</span></span><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>If you&rsquo;re setting this up on a fresh box, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 give you full root access, so nothing here needs any workarounds; you configure CrowdSec exactly like on any Linux server you own.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="choosing-collections-for-your-actual-stack">Choosing collections for your actual stack</h3>
<p>Don&rsquo;t install everything the Hub offers just because it&rsquo;s there. Pull the collections matching what&rsquo;s actually running: <code>crowdsecurity/sshd</code> and <code>crowdsecurity/linux</code> for basically any server, <code>crowdsecurity/nginx</code> or <code>crowdsecurity/traefik</code> for your reverse proxy, <code>crowdsecurity/wordpress</code> if you host WordPress. Extra collections just run extra parsers against logs they&rsquo;ll never match, adding noise and slowing troubleshooting later.</p>
<h3 id="whitelisting-your-own-traffic-before-it-bites-you">Whitelisting your own traffic before it bites you</h3>
<p>The single most common CrowdSec support request is &ldquo;I locked myself out.&rdquo; Scenarios don&rsquo;t know the difference between an attacker bruteforcing SSH and a developer who fat-fingered their password four times in a row. Before you go live, add your office and home IPs, your CI runners, and any monitoring service hitting your endpoints on a schedule, to a whitelist parser. It takes two minutes and saves an awkward out-of-band recovery later.</p>
<h3 id="wiring-a-bouncer-that-actually-enforces-decisions">Wiring a bouncer that actually enforces decisions</h3>
<p>This is the step people skip, and it&rsquo;s the one that matters. A decision that nothing enforces is a log entry, not a defense. Whether you use the firewall bouncer, the Nginx bouncer, or the Traefik plugin, verify with <code>cscli decisions list</code> that bans are being created, then verify separately that the bouncer is actually querying the LAPI and dropping traffic. Detection without enforcement is a security theater prop.</p>
<h3 id="deciding-on-the-community-blocklist-and-the-console">Deciding on the community blocklist and the Console</h3>
<p>Opting into the shared blocklist is where CrowdSec earns its name, and for most people it&rsquo;s a clear win: you get ahead of IPs before they hit you. It does mean your instance reports confirmed attacker IPs upstream, so if that data-sharing model doesn&rsquo;t fit your compliance posture, run CrowdSec entirely locally and skip it, you just lose the network effect. The Console is a separate, genuinely optional layer for multi-machine visibility; the engine works identically with or without it.</p>
<h2 id="where-crowdsec-is-not-the-right-answer">Where CrowdSec is not the right answer</h2>
<p>It&rsquo;s worth being honest about the edges. CrowdSec is not a SIEM: no long-term log retention, no correlation across event types, no compliance reporting, it&rsquo;s a detection-and-decision engine focused on network-level bad behavior. It&rsquo;s not a WAF replacement either; deep application-layer inspection, like recognizing a SQL injection attempt inside a specific POST body, is a different problem than &ldquo;this IP is scanning.&rdquo; It depends entirely on bouncers being deployed, an instance with no bouncer attached is just an expensive log reader. It also depends on decent parsers existing for your exact log format; an unusual application log with no matching Hub parser gets nothing out of the box until you write one. And none of this substitutes for patching: it can slow down a scanner probing a known CVE, but an unpatched vulnerability still exposes you to a patient attacker who never trips the bruteforce or scanning scenarios at all.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><a href="https://github.com/fail2ban/fail2ban" rel="nofollow">fail2ban</a>
 is the obvious comparison, and still a perfectly reasonable choice for a single box where you don&rsquo;t need shared threat intelligence: simpler, no LAPI, no bouncer concept, just log-watching and local bans. What it doesn&rsquo;t give you is CrowdSec&rsquo;s network effect or its Hub ecosystem of maintained scenarios.</p>
<p><a href="https://github.com/wazuh/wazuh" rel="nofollow">Wazuh</a>
 sits at a completely different weight class: a full XDR/SIEM platform with file integrity monitoring, vulnerability detection, compliance reporting, and centralized log management across a fleet. If you need SIEM-grade visibility and the operational budget to run it, Wazuh is a serious tool; for a handful of VPS instances it&rsquo;s usually more infrastructure than the problem calls for.</p>
<p><a href="https://github.com/owasp-modsecurity/ModSecurity" rel="nofollow">ModSecurity</a>
 operates at a different layer entirely: an application-layer WAF inspecting HTTP request and response bodies against rule sets like OWASP CRS, catching SQL injection and XSS payloads inside the request itself. CrowdSec is behavior-based and network-focused; ModSecurity is content-based and application-focused. Complementary rather than competing, running both isn&rsquo;t redundant.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If your current defense is either nothing or a lonely fail2ban jail rediscovering the same botnets since 2019, CrowdSec is a straightforward upgrade: free, open source, and genuinely useful the moment you install a couple of collections and wire up one bouncer. The catch is that it only does anything if you finish the job: pick the right collections, whitelist your own traffic, and deploy a bouncer that actually enforces what it decides. Start with a VPS you control end-to-end (our <a href="/en/virtual-servers/vps/">VPS plans</a>
 give you root access to set this up exactly as described above), and if you&rsquo;d rather someone else own the hardening, patching, and monitoring entirely, that&rsquo;s what our <a href="/en/services/server-administration/">server administration service</a>
 is for.</p>
]]></content:encoded><category>Security</category></item><item><title>NetBox: the source of truth behind our own infrastructure (DCIM + IPAM)</title><link>https://dreamserver.ro/en/blog/netbox-dcim-ipam/</link><pubDate>Sat, 30 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/netbox-dcim-ipam/</guid><description>How we use NetBox as our real CMDB and IPAM for our Bucharest datacenter, queried live by our own Ansible automation. Plus a VPS install guide.</description><content:encoded><![CDATA[<p>The most widely deployed IPAM in the industry is a spreadsheet named <code>final-ip-list-v3-FINAL.xlsx</code>, sitting on the desktop of someone who left the company two years ago. A close second is tribal knowledge: &ldquo;that /24 belongs to Andrei, but Andrei switched jobs, so ask Bogdan, who probably knows what&rsquo;s still free.&rdquo; Neither has an API. Neither validates itself. And neither tells you the truth once someone allocates an address by hand and forgets to update the sheet.</p>
<p>NetBox (<a href="https://github.com/netbox-community/netbox" rel="nofollow">github.com/netbox-community/netbox</a>
, <a href="https://netboxlabs.com/" rel="nofollow">netboxlabs.com</a>
, Apache-2.0 licensed) solves exactly this problem: it&rsquo;s the de facto open-source tool for DCIM (data center infrastructure management) and IPAM (IP address management), combined into a single, queryable, API-driven system. This isn&rsquo;t a passing technical curiosity for us: NetBox is DreamServer&rsquo;s own CMDB and IPAM, the actual source of truth that our Ansible-based automation reads from, and writes to, every time we provision something. When we spin up a new virtual machine across our Proxmox virtualization clusters, IP allocation and device registration happen through NetBox, not inside someone&rsquo;s head or a local spreadsheet. Before NetBox, infrastructure knowledge was tribal and spreadsheets were already stale by the time you opened them. Now it&rsquo;s queryable, API-driven, and our automation genuinely reads from it.</p>
<h2 id="what-netbox-actually-is">What NetBox actually is</h2>
<p>NetBox is a Django web application backed by PostgreSQL that models both the physical and logical layers of your infrastructure: sites, racks, devices, cabling, power, IP addresses, prefixes, VLANs, VRFs, provider circuits, autonomous system numbers, and virtual machines with their clusters. Every one of these objects links to the others: a device sits in a rack, a rack sits in a site, a device has interfaces, interfaces hold IP addresses, IP addresses belong to prefixes, and prefixes belong to VRFs or VLANs. It&rsquo;s not a flat database of independent rows, it&rsquo;s a graph of your actual network reality.</p>
<h3 id="why-the-dcim--ipam-combination-matters">Why the DCIM + IPAM combination matters</h3>
<p>Most teams split these two worlds apart: one tool (or spreadsheet) for &ldquo;what hardware do we have and where,&rdquo; and another tool (or spreadsheet) for &ldquo;which IP is assigned to what.&rdquo; The problem is that these two worlds are really one world, seen from two angles. An IP address doesn&rsquo;t exist in a vacuum. It&rsquo;s assigned to an interface, which belongs to a device, which sits in a rack, which sits in a site. When DCIM and IPAM live in the same data model, you can ask questions like &ldquo;which prefixes are free in this site&rdquo; or &ldquo;which device is using this IP&rdquo; without mentally joining two systems that have never heard of each other. That&rsquo;s the whole idea of a source of truth: one place to ask, one correct answer, instead of two places that can quietly disagree.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>NetBox ships with data models already built for nearly everything that data center and network infrastructure involves:</p>
<ul>
<li><strong>Devices, racks, cables</strong> - physical inventory, with exact rack positions and cable-to-cable connections documented.</li>
<li><strong>Power</strong> - panels, feeds, outlets, with power allocated per device.</li>
<li><strong>Sites and locations</strong> - a hierarchy of facilities, rooms, and rows of racks.</li>
<li><strong>Virtual machines and clusters</strong> - for virtualization environments, so VMs aren&rsquo;t ghosts nobody tracked.</li>
<li><strong>IP addresses and prefixes</strong> - hierarchical allocation, with status (active, reserved, deprecated) per address.</li>
<li><strong>VLANs and VRFs</strong> - network segmentation that&rsquo;s documented, not just configured on a switch and forgotten.</li>
<li><strong>Circuits and providers</strong> - the upstream transport connections and the carriers operating them.</li>
<li><strong>Autonomous system numbers</strong> - relevant if you run your own routing space, as we do.</li>
<li><strong>REST and GraphQL APIs</strong> - anything you can do through the web UI, you can also do programmatically.</li>
<li><strong>Webhooks</strong> - events fired when something changes, useful for driving automated syncs to other systems.</li>
<li><strong>Custom fields</strong> - extend any data model with attributes specific to your organization, without touching code.</li>
<li><strong>Change logging</strong> - a full history of who changed what and when.</li>
</ul>
<h2 id="installing-netbox-on-a-vps">Installing NetBox on a VPS</h2>
<p>The realistic install path today is the official <code>netbox-community/netbox-docker</code> project, which brings up NetBox alongside PostgreSQL and Redis (Redis handles caching and task queues, it&rsquo;s not optional):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone -b release https://github.com/netbox-community/netbox-docker.git
</span></span><span style="display:flex;"><span>cd netbox-docker
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># create an override file with your settings (ports, superuser, etc.)</span>
</span></span><span style="display:flex;"><span>cp docker-compose.override.yml.example docker-compose.override.yml
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># bring up the stack: netbox, netbox-worker, postgres, redis, redis-cache</span>
</span></span><span style="display:flex;"><span>docker compose pull
</span></span><span style="display:flex;"><span>docker compose up -d
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># create your first superuser</span>
</span></span><span style="display:flex;"><span>docker compose exec netbox /opt/netbox/netbox/manage.py createsuperuser
</span></span></code></pre></div><p>The compose file brings up every container you need (NetBox, its worker for async tasks, PostgreSQL, Redis). If you&rsquo;re exposing the app port directly on a VPS, use <code>ufw</code> (or the equivalent firewall) to restrict access to where you actually need it, and put a TLS-terminating reverse proxy in front (Nginx or Traefik) instead of leaving the container exposed directly. NetBox holds the complete map of your infrastructure, IP addresses included, so it&rsquo;s not the kind of service you want sitting on the open internet without authentication and encryption in transit. If you&rsquo;d rather not manage the VPS, firewall, and OS maintenance yourself, our <a href="/en/virtual-servers/vps/">VPS page</a>
 has dedicated resources that fit exactly this kind of self-hosted app.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="model-sites-racks-and-devices-before-you-throw-ips-at-the-system">Model sites, racks, and devices before you throw IPs at the system</h3>
<p>The most common mistake is jumping straight to prefixes and IP addresses while skipping the physical hierarchy. NetBox is far more useful when the model is complete: site, then racks, then devices, then interfaces, and only then IP addresses attached to those interfaces. Skip that and you end up with a decent IPAM that has no connection to physical reality, which is half the value you&rsquo;re leaving on the table.</p>
<h3 id="keep-prefixes-and-vlans-consistent-from-day-one">Keep prefixes and VLANs consistent from day one</h3>
<p>Allocate prefixes hierarchically (a parent /24, then child subnets per VLAN or function) and tie every VLAN to its real prefix. Do this consistently and NetBox instantly shows you what space is free, what&rsquo;s allocated, and where overlaps exist, without you counting rows in a table by hand.</p>
<h3 id="custom-fields-for-organization-specific-metadata">Custom fields for organization-specific metadata</h3>
<p>You&rsquo;ll always have data that doesn&rsquo;t fit the standard model: who owns a given service, an internal project code, a contract expiry date tied to a specific rack. Custom fields let you extend any model (device, prefix, VLAN, whatever) without bloating the schema with fields nobody else needs. Resist the temptation to dump this into a free-text description field instead, you can&rsquo;t query that efficiently.</p>
<h3 id="drive-automation-from-the-api-not-repeated-manual-edits">Drive automation from the API, not repeated manual edits</h3>
<p>This is the piece that turns NetBox from a nice-looking database into something genuinely useful: when your automation (Ansible, Terraform, whatever you run) reads its dynamic inventory from NetBox and writes IP allocations back through the REST or GraphQL API, the data stays in sync with reality because the automation is what creates that reality. Layer in webhooks if you want other systems to react (notifications, syncs, tickets) the moment something changes in NetBox, instead of waiting for a human to notice.</p>
<h2 id="where-netbox-is-not-the-right-answer">Where NetBox is not the right answer</h2>
<p>NetBox is not a live network monitoring tool. It won&rsquo;t tell you whether a switch is down right now or whether an interface is dropping packets; you need a separate monitoring and alerting system for that. NetBox describes how your infrastructure should look, not how it&rsquo;s behaving in real time this second.</p>
<p>It&rsquo;s also not a configuration management or automation engine by itself. It&rsquo;s the data, not the execution. You can use it as the source that Ansible, Terraform, or anything else reads from, but it won&rsquo;t push configuration to your gear on its own.</p>
<p>It&rsquo;s overkill if you&rsquo;re running a handful of servers on a single subnet. Its value grows with the number of devices, VLANs, and people involved; below a certain size, a well-maintained spreadsheet can genuinely hold up for a while.</p>
<p>And most importantly: NetBox does not discover your network on its own. It doesn&rsquo;t scan your infrastructure and populate itself automatically (there are integrations and scripts that can help, but that&rsquo;s not the default behavior). If you don&rsquo;t have the discipline to enter data correctly up front and keep it current whenever you make manual changes, you end up with a source of truth that lies, which is worse than having no IPAM at all, because now people trust it wrongly.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://phpipam.net/" rel="nofollow">phpIPAM</a>
</strong> - a lighter IPAM with no DCIM ambitions, a good fit if all you need is IP address management without racks and physical devices.</li>
<li><strong><a href="https://www.racktables.org/" rel="nofollow">Racktables</a>
</strong> - leans more toward the DCIM side (racks, devices, cabling), with IPAM as a secondary feature. The interface shows its age compared to NetBox, but it works.</li>
<li><strong><a href="https://github.com/nautobot/nautobot" rel="nofollow">Nautobot</a>
</strong> - a fork of NetBox, born from the same codebase, with extra emphasis on plugins and integration with network automation platforms. If you&rsquo;re already deep in the NetBox ecosystem and need more plugin flexibility, it&rsquo;s worth a direct comparison.</li>
</ul>
<p>We stayed with NetBox because it covers both DCIM and IPAM in the same data model, with a mature API and a large community behind it, which matters when you&rsquo;re integrating it with your own automation for the long haul.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;re managing more than a handful of servers and feel like your infrastructure lives inside one person&rsquo;s head (often your own), NetBox is worth setting up before it becomes a problem, not after. You can get it running quickly on a <a href="/en/virtual-servers/vps/">VPS</a>
 with dedicated resources, behind a TLS-terminating reverse proxy. If you&rsquo;d rather have someone else handle the operating system, firewall, and updates while you focus on modeling your infrastructure, our <a href="/en/services/server-administration/">server administration</a>
 team can take that part off your plate. And if you&rsquo;re at the stage of putting your entire <a href="/en/network/">network</a>
 in order, NetBox is where you start writing the truth down instead of just remembering it.</p>
]]></content:encoded><category>Network</category><category>IPAM</category></item><item><title>Gatus: health monitoring with config that lives in Git</title><link>https://dreamserver.ro/en/blog/gatus-status-page-health-monitoring/</link><pubDate>Wed, 27 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/gatus-status-page-health-monitoring/</guid><description>How we run Gatus, an open source health dashboard, to watch dozens of services across our fleet, all defined in YAML and version-controlled in Git.</description><content:encoded><![CDATA[<p>A service goes down at 3 a.m. It doesn&rsquo;t matter how clean the code is or how expensive the server is, something eventually breaks: a certificate expires, a database refuses connections, a container gets stuck in a crash loop. The only question that actually matters is how fast you find out, before a customer does, through an angry ticket at 9 a.m.</p>
<p>Our answer to that question is <a href="https://github.com/TwiN/gatus" rel="nofollow">Gatus</a>
, a health-check and status page dashboard, open source, permissively licensed (MIT-style), written in Go, and shipped as a single lightweight binary or container image you can drop almost anywhere. This isn&rsquo;t just something we recommend to others: we actually run it in our own datacenter in Bucharest, as an internal tool, to know instantly when something in our own infrastructure, or in a customer-facing service, goes down, before someone outside has to tell us.</p>
<h2 id="what-gatus-actually-is">What Gatus actually is</h2>
<p>Gatus isn&rsquo;t an agent you install on every machine to report system metrics. It&rsquo;s not competing with a node exporter or a full-stack monitoring agent. It does one thing, and it does it well: it periodically checks a set of endpoints (HTTP, TCP, ICMP, DNS, TLS) and evaluates the response against conditions you write yourself, in YAML.</p>
<p>A condition looks like this: <code>[STATUS]==200</code>, <code>[RESPONSE_TIME]&lt;300</code>, <code>[CERTIFICATE_EXPIRATION]&gt;168h</code>. Easy to read, easy to write, and flexible enough to cover most real-world scenarios: &ldquo;respond with 200 in under 300ms&rdquo;, &ldquo;the TLS certificate still has at least a week of validity&rdquo;, &ldquo;the port is open&rdquo;, &ldquo;the DNS record resolves correctly&rdquo;.</p>
<p>Results are kept in SQLite, so you don&rsquo;t need an external database just to hold the history, and Gatus also exposes a Prometheus-compatible <code>/metrics</code> endpoint, so you can wire it into an existing monitoring stack if you want to correlate the data with other metrics.</p>
<h3 id="why-this-actually-matters-in-production">Why this actually matters in production</h3>
<p>The real difference between this and &ldquo;I put a ping in cron&rdquo; comes down to two things: configurable failure/success thresholds, and built-in alerting integrations. You don&rsquo;t want an alert on the first timeout (networks have blips), but you also don&rsquo;t want to find out about a real outage 20 minutes later. Gatus lets you define exactly how many consecutive failures mean &ldquo;this is actually down&rdquo; and how many consecutive successes mean &ldquo;this has actually recovered&rdquo;, per endpoint. And once that threshold is hit, the alert goes out over email, Slack, Telegram, PagerDuty, or other supported channels, without you having to write your own debounce logic.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li>A ready-to-use web status page showing the current state and recent history of every monitored endpoint.</li>
<li>Multi-protocol checks: HTTP/HTTPS, TCP, ICMP (ping), DNS, and TLS/certificate validation, all from the same configuration format.</li>
<li>Scriptable conditions on status code, response time, body content, certificate expiration, and other fields exposed by the response.</li>
<li>Configurable failure-threshold and success-threshold, so you control alerting noise instead of it controlling you.</li>
<li>Ready-made notification integrations for the most common channels (email, Slack, Telegram, PagerDuty, and others).</li>
<li>A Prometheus-format <code>/metrics</code> endpoint, for anyone who wants to feed the data into an existing stack.</li>
<li>Zero external database: history lives in SQLite, right next to the binary.</li>
<li>Fully declarative configuration: everything you monitor is defined in YAML files, which means you can put the config in Git, review a pull request that adds a new endpoint, and have a complete change history for your monitoring, exactly like any other piece of infrastructure-as-code.</li>
</ul>
<h2 id="installing-gatus-on-a-vps">Installing Gatus on a VPS</h2>
<p>The simplest way to try Gatus is with Docker, directly on a VPS. All you need is a configuration file mounted as a volume and one exposed port.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/gatus/config
</span></span><span style="display:flex;"><span>cat &gt; ~/gatus/config/config.yaml <span style="color:#e6db74">&lt;&lt;&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">endpoints:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  - name: website
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    url: &#34;https://example.com&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    interval: 60s
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    conditions:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - &#34;[STATUS] == 200&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - &#34;[RESPONSE_TIME] &lt; 500&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - &#34;[CERTIFICATE_EXPIRATION] &gt; 168h&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  - name: api-backend
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    url: &#34;tcp://example.com:5432&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    interval: 60s
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    conditions:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      - &#34;[CONNECTED] == true&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name gatus <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 8080:8080 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v ~/gatus/config:/config <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  twinproduction/gatus
</span></span></code></pre></div><p>Or, if you prefer docker-compose, the equivalent is:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">gatus</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">twinproduction/gatus</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">gatus</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;8080:8080&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./config:/config</span>
</span></span></code></pre></div><p>One thing worth keeping in mind from the start: if the VPS has <code>ufw</code> enabled (as any internet-facing server should), port 8080 isn&rsquo;t opened automatically just because the container is running. Either explicitly open the port with <code>ufw allow 8080/tcp</code> (and restrict it to your own IPs if you can), or, better yet, put Gatus behind a reverse proxy with TLS and leave only 80/443 open, keeping 8080 reachable only from the server&rsquo;s internal network. For a status page you and your team check, the second option is almost always the right call.</p>
<p>If you don&rsquo;t already have a box to run this on, spin one up quickly on a <a href="/en/virtual-servers/vps/">VPS</a>
 with enough resources for a small container, Gatus has no serious hardware requirements.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="reasonable-failure-and-success-thresholds">Reasonable failure and success thresholds</h3>
<p>Don&rsquo;t set <code>failure-threshold: 1</code> unless you genuinely want an alert on the first network blip. In practice, a threshold of 2-3 consecutive failures, at a 30-60 second check interval, gives you an honest signal without waking you up for every transient glitch. Symmetrically, a <code>success-threshold</code> of 2 confirms the recovery is real, not just one lucky response sandwiched between two outages.</p>
<h3 id="certificates-that-expire-quietly">Certificates that expire quietly</h3>
<p>The condition <code>[CERTIFICATE_EXPIRATION] &gt; 168h</code> (one week) on every HTTPS endpoint you monitor is probably the cheapest insurance you can configure. An expired certificate discovered by a customer before you is an unpleasant conversation that a single line of YAML avoids entirely.</p>
<h3 id="checks-beyond-http">Checks beyond HTTP</h3>
<p>Not everything that matters answers on port 80/443. A database on TCP, a DNS record that needs to resolve to the right IP, an ICMP ping to a network device, a TLS certificate on a non-web port, all of these fit into the same list of endpoints, with the same kind of conditions. In practice, a single tool covers both your web application and the infrastructure behind it.</p>
<h3 id="alert-by-group-not-per-individual-endpoint">Alert by group, not per individual endpoint</h3>
<p>Once you have dozens of endpoints, per-service alerting can become noise in its own right. Group endpoints logically (public-facing, internal, database, network) and think about the right notification channel for each group: a Slack channel for &ldquo;worth watching&rdquo; alerts, PagerDuty or direct email for the ones that genuinely need immediate action.</p>
<h2 id="where-gatus-is-not-the-right-answer">Where Gatus is not the right answer</h2>
<p>Gatus is excellent at one thing: &ldquo;is this endpoint available and responding the way I expect?&rdquo;. Don&rsquo;t try to use it as a substitute for a full observability stack. It doesn&rsquo;t do distributed tracing, it won&rsquo;t tell you why latency spiked on a specific database query, it doesn&rsquo;t profile your application, and it doesn&rsquo;t replace a centralized logging system. If you need to understand an application&rsquo;s internal behavior, not just whether it responds from the outside, you need application metrics, structured logs, and probably tracing, alongside Gatus, not instead of it.</p>
<p>Also, if your team already has a mature Prometheus/Grafana stack with blackbox checks configured, you might not gain much by bolting on Gatus separately, beyond a dedicated status page that&rsquo;s easier to show to people who don&rsquo;t read PromQL dashboards.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><a href="https://github.com/louislam/uptime-kuma" rel="nofollow">Uptime Kuma</a>
: probably the best-known alternative, with a web interface much more oriented toward click-and-configure. If you&rsquo;d rather add monitors from a UI than from version-controlled YAML files, Uptime Kuma is a solid choice. The trade-off is that configuration history lives in the application&rsquo;s own database, not in Git, which makes code review and change tracking harder.</li>
<li><a href="https://github.com/prometheus/blackbox_exporter" rel="nofollow">Prometheus Blackbox Exporter</a>
: if you already run Prometheus and just want probe-style checks (HTTP, TCP, ICMP, DNS) integrated directly into that ecosystem, without a separate status page, blackbox exporter does exactly that, but it stays one more piece inside a Prometheus stack rather than a standalone product with its own UI.</li>
<li><a href="https://github.com/healthchecks/healthchecks" rel="nofollow">Healthchecks.io</a>
: a different angle, focused on monitoring cron jobs and scheduled tasks (a &ldquo;dead man&rsquo;s switch&rdquo;: if the job doesn&rsquo;t check in on time, you get alerted), rather than active endpoint checks. It can be self-hosted, and pairs well with Gatus as a complement, not necessarily a replacement.</li>
</ul>
<p>The reason we picked Gatus was simple: config-as-YAML-in-Git matches exactly how we treat the rest of our infrastructure, as infrastructure-as-code, reviewed in pull requests, not quietly edited from a UI that doesn&rsquo;t remember who changed what.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;re a small team or a solo developer who wants to be the first to know, not the last, when something breaks, Gatus is a solid choice: easy to install, easy to understand, and its configuration lives next to the rest of your code, not in a separate database. It&rsquo;s not a substitute for full observability, but for the basic question, &ldquo;is this service actually up right now&rdquo;, it gets the job done without unnecessary complexity.</p>
<p>If you need a place to run it without the hassle, a <a href="/en/virtual-servers/vps/">VPS</a>
 is more than enough for a dashboard like this, and if you&rsquo;d rather not deal with the server administration side yourself, firewall setup, updates, hardening, our <a href="/en/services/server-administration/">server administration</a>
 team can take over the operational part while you focus on what the dashboard shows.</p>
]]></content:encoded><category>Monitoring</category></item><item><title>CloudPanel: a free server panel that does not feel cheap</title><link>https://dreamserver.ro/en/blog/cloudpanel-free-server-control-panel/</link><pubDate>Sat, 23 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/cloudpanel-free-server-control-panel/</guid><description>A practical look at CloudPanel, the free open-source server control panel from MGT-COMMERCE: what is in the box, how it installs on a Debian or Ubuntu VPS, and where it stops short.</description><content:encoded><![CDATA[<p>If you have ever priced cPanel for a small fleet of VPSes, you already know the math is unkind. The per-account license, on top of the OS license if you went RHEL, on top of the server itself, turns a 5 EUR VPS into a 25 EUR VPS before you have served a single request. Plesk is in the same neighbourhood. The free alternatives have a habit of being either ten years behind on UI or quietly suspect about telemetry, which leaves a real gap for &ldquo;modern panel, free, hosted on my own box&rdquo;.</p>
<p><a href="https://www.cloudpanel.io/" rel="nofollow">CloudPanel</a>
, the open-source panel from MGT-COMMERCE, has been settling into that gap for the last couple of years. The community edition is the only edition, which is a refreshing change from the freemium pattern. As of v2.5.3 (December 2025) it sits at roughly 1.8k stars on <a href="https://github.com/cloudpanel-io/cloudpanel-ce" rel="nofollow">GitHub</a>
 and ships cloud images for every major hyperscaler. We have been running it on a <a href="/en/virtual-servers/vps/">VPS in our Bucharest datacenter</a>
 for several weeks. This is the field report.</p>
<h2 id="what-cloudpanel-actually-is">What CloudPanel actually is</h2>
<p>Architecturally, CloudPanel is <strong>a thin management layer over a hardened Nginx, PHP-FPM, MySQL or MariaDB stack</strong>. It is not its own web server, not its own database, not a Docker swarm or a Kubernetes wrapper. The panel writes Nginx vhosts, manages PHP-FPM pools per site, provisions Linux users so each site is isolated at the OS level, and exposes a UI to do all of that without you remembering the exact <code>php-fpm</code> config knob you wanted last Tuesday.</p>
<p>If that sounds dull, that is the point. The interesting design decision in CloudPanel is what it does <em>not</em> try to be: it is not a hosting reseller platform, not a billing system, not a mail server, not a DNS authoritative server. Those omissions are deliberate, and they are also the first thing to understand before you adopt it.</p>
<h3 id="the-stack-you-get">The stack you get</h3>
<ul>
<li><strong>Web tier</strong>: Nginx, configured as the front for every site. There is no Apache option.</li>
<li><strong>PHP</strong>: multiple versions installed in parallel (7.x and 8.x), switchable per site with a click. Running an old PrestaShop on 7.4 next to a Laravel 11 app on 8.3 on the same box is a non-event.</li>
<li><strong>Databases</strong>: MySQL 8.4, 8.0, 5.7 or MariaDB 11.8, 11.4, 10.11, 10.6. You pick one at install time via a <code>DB_ENGINE</code> variable, the rest are not installed.</li>
<li><strong>Application types</strong>: PHP, Node.js, Python, static sites, and reverse-proxy passthroughs (handy for putting a panel-managed Nginx in front of an app you run elsewhere).</li>
<li><strong>TLS</strong>: Let&rsquo;s Encrypt baked in, with HTTP-01 validation and auto-renewal.</li>
</ul>
<p>The control plane itself is a Symfony PHP app behind its own Nginx vhost on port 8443. That single port is the entire management surface; close it to the world and the only people who can touch the panel are people on your management network.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The community edition (which, again, is the only edition) ships with:</p>
<ul>
<li><strong>Site isolation</strong> at the OS level. Each site gets its own Linux user, its own PHP-FPM pool, its own home directory. A compromised WordPress on site A does not have read access to site B&rsquo;s files. This is the single feature that matters most for shared environments.</li>
<li><strong>Free Let&rsquo;s Encrypt SSL</strong>, one click per site, auto-renewed by cron.</li>
<li><strong>UFW firewall</strong> management with a sane default policy and a UI to add rules.</li>
<li><strong>Two-factor authentication</strong> for the panel admin, TOTP based.</li>
<li><strong>IP and Bot blocker</strong>, which lets you blacklist user agents and IP ranges without dropping into Nginx config.</li>
<li><strong>Cloudflare integration</strong>, primarily for trusting the right <code>X-Forwarded-For</code> chain so your access logs are not full of Cloudflare edge IPs.</li>
<li><strong>Vhost templates</strong> per app type, pre-configured for WordPress, Magento, Symfony, Node, generic PHP, and so on. The templates are sensible (gzip, brotli, security headers) and editable.</li>
<li><strong>Per-site cron</strong>, FTP, file manager, and database management UI.</li>
<li><strong>Backup destinations</strong> to S3-compatible buckets, including MinIO and Backblaze B2, configurable per site.</li>
</ul>
<p>What it does <em>not</em> ship: a mail server, a DNS authoritative server, a billing/customer portal, a one-click app installer like Softaculous, or any kind of multi-server clustering. If your mental model of a control panel is &ldquo;cPanel feature parity&rdquo;, CloudPanel will feel sparse. If your mental model is &ldquo;the smallest thing that lets me stop hand-editing Nginx vhosts&rdquo;, it will feel right-sized.</p>
<h2 id="installing-cloudpanel-on-a-vps">Installing CloudPanel on a VPS</h2>
<p>The install path is genuinely a one-liner and the docs do not pretend otherwise. Minimum sane resources are <strong>1 vCPU, 2 GB RAM, 10 GB disk</strong>. Supported OSes are Debian 11, 12, 13 and Ubuntu 22.04, 24.04 LTS, on x86_64 or arm64. Anything older than Debian 11 is not supported and you should not try.</p>
<p>On a fresh server, log in as root and update the base packages first:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>apt update <span style="color:#f92672">&amp;&amp;</span> apt -y upgrade <span style="color:#f92672">&amp;&amp;</span> apt -y install curl wget sudo
</span></span></code></pre></div><p>Then run the installer. The exact command from the official docs (with sha256 verification, which you should not skip):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -sS https://installer.cloudpanel.io/ce/v2/install.sh -o install.sh; <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>echo <span style="color:#e6db74">&#34;6eac061df80f08b75224fcd7fce2f115e201696d8a6122e31abf7259a813b462 install.sh&#34;</span> | <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>sha256sum -c <span style="color:#f92672">&amp;&amp;</span> sudo DB_ENGINE<span style="color:#f92672">=</span>MYSQL_8.4 bash install.sh
</span></span></code></pre></div><p>Pick the <code>DB_ENGINE</code> value that matches your needs. Common choices are <code>MYSQL_8.4</code>, <code>MYSQL_8.0</code>, <code>MARIADB_11.8</code>, <code>MARIADB_10.11</code>. If you want MariaDB because your app stack assumes it, decide now, the database engine is not swappable post-install without manual surgery.</p>
<p>The script will install Nginx, PHP, the chosen database, the panel itself, and the supporting cron jobs. It takes 3 to 6 minutes on a 1 vCPU VPS, longer on slow disks.</p>
<p>Once it finishes, the management UI is at <code>https://&lt;your-server-ip&gt;:8443/</code>. The first request lands on a setup wizard where you create the admin user, <strong>and that is the moment of risk</strong>: between the install finishing and you completing the wizard, anyone who can reach port 8443 can claim the admin account. The official guidance is to access the panel &ldquo;as fast as possible&rdquo;. The better guidance is to firewall port 8443 down to your own IP before you even start the install:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow from YOUR_OFFICE_IP to any port <span style="color:#ae81ff">8443</span>
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">8443</span>
</span></span></code></pre></div><p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, put the same rule in your provider-side network firewall too. Host-level rules are fine, network-level rules are better, both is correct.</p>
<p>After the wizard, turn on TOTP two-factor for the admin account from <strong>Account</strong> &gt; <strong>Security</strong>. There is no excuse not to.</p>
<h2 id="adding-a-real-php-site-with-ssl">Adding a real PHP site with SSL</h2>
<p>The mental model is: every site is an isolated user with a dedicated docroot under <code>/home/&lt;sitename&gt;/htdocs/&lt;domain&gt;/</code>. You do not edit Nginx vhosts by hand, the panel does that for you when you create or modify a site.</p>
<p>In the UI, <strong>Sites</strong> &gt; <strong>Add site</strong>:</p>
<ul>
<li><strong>Site type</strong>: pick the closest match (WordPress, Magento, Symfony, generic PHP, Node.js, static, reverse proxy). The template prefills sensible defaults for that app type.</li>
<li><strong>Domain</strong>: <code>example.com</code> plus any aliases. Wildcard subdomains are supported via DNS-01 if you wire it up to Cloudflare or Route53; otherwise stick to explicit names.</li>
<li><strong>PHP version</strong>: 7.4 through 8.3 in the dropdown. Switchable later.</li>
<li><strong>Site user</strong>: the Linux user that will own this site. Defaults to a sane derivation of the domain.</li>
</ul>
<p>Save, point your A/AAAA records at the panel host, then come back and click <strong>SSL/TLS</strong> &gt; <strong>Let&rsquo;s Encrypt</strong>. The panel does the HTTP-01 dance, drops the cert into <code>/etc/nginx/ssl/</code>, and reloads. Sixty seconds, no manual certbot.</p>
<p>To verify the site is actually live:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -I https://example.com
</span></span></code></pre></div><p>You should see a <code>200</code> (or your app&rsquo;s redirect) and a <code>server: nginx</code> header. If you see Apache or anything other than Nginx, something is wrong with your DNS, not with CloudPanel.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few patterns we have found useful beyond the defaults.</p>
<h3 id="use-the-per-site-php-settings-not-phpini">Use the per-site PHP settings, not php.ini</h3>
<p>CloudPanel exposes <code>memory_limit</code>, <code>max_execution_time</code>, <code>upload_max_filesize</code>, and friends per site in the UI. Use those. Editing the global <code>/etc/php/8.3/fpm/php.ini</code> works exactly once, until your next package update wipes it.</p>
<h3 id="vhost-custom-snippets-not-direct-edits">Vhost custom snippets, not direct edits</h3>
<p>Every site has a <strong>Vhost</strong> &gt; <strong>Custom snippets</strong> field where you can paste extra Nginx directives. Use that for cache rules, redirects, security headers. Do not edit the panel-generated vhost file directly; the next save through the UI will overwrite your edits.</p>
<h3 id="cron-over-web-cron">Cron over web cron</h3>
<p>For WordPress in particular, disable WP-Cron in <code>wp-config.php</code> (<code>define('DISABLE_WP_CRON', true);</code>) and add a real cron entry in the panel&rsquo;s <strong>Cron Jobs</strong> tab. WP-Cron firing on every page load is one of the more common reasons a small WordPress site feels slow under modest load.</p>
<h3 id="backups-panel--filesystem-both">Backups: panel + filesystem, both</h3>
<p>CloudPanel&rsquo;s S3 backups are good for application files and databases. They are not a substitute for a full-VM backup. We pair the panel&rsquo;s backups with <a href="/en/services/proxmox-backup/">Proxmox Backup Server</a>
 snapshots at the hypervisor level, so a &ldquo;rebuild this VPS from scratch&rdquo; recovery is a single restore instead of a panel-reinstall plus a per-site restore.</p>
<h3 id="trust-the-bot-blocker-more-than-your-gut">Trust the bot blocker more than your gut</h3>
<p>The IP and Bot blocker has a curated list of known scrapers and credential stuffers. Turn it on for any public site and check the access logs a week later. The number of requests it silently drops, mostly stale WordPress brute-force toolkits, is consistently higher than people expect.</p>
<h2 id="cloudpanel-vs-the-alternatives">CloudPanel vs the alternatives</h2>
<p>For completeness:</p>
<ul>
<li><strong>cPanel/WHM</strong>: the industry default, mature, expensive, RHEL-centric. If you bill clients per cPanel account and need feature parity with the rest of the cPanel ecosystem (CageFS, JetBackup, WHMCS integration), it is still the right answer. CloudPanel is not trying to compete here.</li>
<li><strong>Plesk</strong>: similar pricing tier to cPanel, friendlier UI, runs on more OSes. If you are already paying for Plesk and it works, there is no reason to switch.</li>
<li><strong>aaPanel</strong>: free, broad feature set, Chinese-origin with an opaque telemetry story that has made some operators nervous. The UI is dense in a way that CloudPanel deliberately is not.</li>
<li><strong>HestiaCP</strong>: free, fork of VestaCP, ships with mail and DNS, more &ldquo;do everything&rdquo; in scope. Older codebase, more rough edges, but the right pick if you specifically need bundled mail and DNS.</li>
<li><strong>CyberPanel</strong>: free community edition, OpenLiteSpeed-centric, optimized for LiteSpeed cache. Good if you have already committed to the LiteSpeed ecosystem; otherwise Nginx is more portable.</li>
</ul>
<p>CloudPanel ended up being the cleanest fit for &ldquo;I want a modern panel that manages Nginx, PHP and databases for a small number of sites, without giving me features I will not use&rdquo;. Your weights might be different.</p>
<h2 id="where-cloudpanel-is-not-the-right-answer">Where CloudPanel is not the right answer</h2>
<p>Worth being honest about the limits.</p>
<ul>
<li><strong>No mail server.</strong> If you need IMAP/SMTP hosting on the same panel, CloudPanel will not give it to you. You will run a separate mail box (or, more sensibly, route mail through a managed provider).</li>
<li><strong>No authoritative DNS.</strong> The panel does not run BIND/PowerDNS for you. You manage DNS at your registrar, at Cloudflare, or on a separate authoritative service. For our customers this is usually fine because we provide DNS at the <a href="/en/lir-services/">LIR/network layer</a>
 anyway.</li>
<li><strong>No multi-server.</strong> One panel manages one server. There is no concept of a cluster, no shared filesystem story, no failover. If you have more than a handful of servers, you are looking at orchestration tooling, not a panel.</li>
<li><strong>No customer billing.</strong> This is not a hosting-reseller platform. If you need to sell hosting plans with invoicing, you bolt the panel onto a separate billing system.</li>
<li><strong>Self-hosting is still self-hosting.</strong> You patch the OS, you watch the disk fill up, you deal with the occasional MySQL surprise. If your team does not want that, <a href="/en/services/server-administration/">a managed service</a>
 is cheaper in operator hours than free software is in your time.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you self-host a handful of PHP, Node or Python sites on a VPS or dedicated server, and you have been hand-editing Nginx vhosts long enough to be bored of it, CloudPanel is worth a Sunday afternoon. The install is a single command, the UI gets out of your way, and the per-site Linux user model means a compromised plugin on one site is not a panel-wide incident.</p>
<p>If you are running it in production, put it on a server with a real backup story (Proxmox Backup, snapshots, your call), firewall the management port, and treat the panel as critical infrastructure. The Nginx and PHP configs the panel writes are easy to rebuild; the database of site definitions and users is what you would actually miss.</p>
<p>We run CloudPanel ourselves in front of a few internal sites and recommend it to customers who ask for &ldquo;something between bare Nginx and cPanel&rdquo;. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 start small enough to make the experiment cheap, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install and tuning if you would rather skip the learning curve. If something does not fit, <a href="/en/contact/">get in touch</a>
 and we will tell you honestly whether it is the right tool for the job.</p>
<p>The code is on <a href="https://github.com/cloudpanel-io/cloudpanel-ce" rel="nofollow">GitHub</a>
, the install is one command, and you will know within an hour whether it earns a spot in your stack.</p>
]]></content:encoded><category>Control Panels</category><category>Hosting</category></item><item><title>SafeLine WAF: a serious open-source firewall for your web apps</title><link>https://dreamserver.ro/en/blog/safeline-waf-open-source-self-hosted/</link><pubDate>Wed, 20 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/safeline-waf-open-source-self-hosted/</guid><description>A hands-on look at SafeLine, the self-hosted WAF from Chaitin Tech: semantic detection engine, install on a VPS in five minutes, real configuration examples and the limits to know before you put it in front of production.</description><content:encoded><![CDATA[<p>If you have ever stared at a 5xx graph at 3 AM while a botnet pokes every login form you own, you already know why a Web Application Firewall matters. The honest question is which one to put in front of your stack without paying Cloudflare Enterprise money or fighting ModSecurity rulesets for a weekend.</p>
<p><a href="https://github.com/chaitin/SafeLine" rel="nofollow">SafeLine</a>
, the open-source WAF maintained by <a href="https://www.chaitin.com/" rel="nofollow">Chaitin Tech</a>
, has been quietly building momentum around that exact gap. As of v9.3.4 (released 17 April 2026) it sits at 21k+ stars on GitHub, runs on more than 180,000 installations and is licensed under GPL-3.0. We have been testing it on a VPS in our Bucharest datacenter for a few weeks. This post is the field report.</p>
<h2 id="what-safeline-actually-is">What SafeLine actually is</h2>
<p>Architecturally, SafeLine is a <strong>reverse proxy with a detection engine in front of your origin</strong>. Traffic hits SafeLine on ports 80/443, gets inspected, and either passes through to your upstream application or is challenged, throttled, or blocked. The reverse proxy is built on a hardened Tengine fork (Tengine being Alibaba&rsquo;s nginx derivative), with detection logic implemented as native modules in C++ and Lua. The management plane is a Go service backed by PostgreSQL.</p>
<p>If that sounds a lot like nginx + ModSecurity, the comparison is fair on paper and unfair in practice. The interesting part is what happens between request arrival and the upstream call.</p>
<h3 id="why-the-detection-engine-is-different">Why the detection engine is different</h3>
<p>Most open-source WAFs you have probably touched, ModSecurity with the OWASP Core Rule Set being the canonical example, are fundamentally regex engines. They match patterns in the request and decide. The trade-off is well known: tighten the rules and you start blocking legitimate users, loosen them and attacks slip through. Anyone who has ever paranoia-mode&rsquo;d ModSecurity in front of a WordPress site knows the false-positive tax.</p>
<p>SafeLine takes a different route. It parses the request as a syntactic structure (SQL grammar for query parameters that look database-shaped, JS AST for script content, shell parser for command patterns) and then evaluates whether the parsed result has the <em>semantic shape</em> of an attack. A query with quotes is not automatically suspicious; a query whose parsed form would change SQL execution flow is. The Chaitin team publishes benchmark numbers around 99.4% true-positive accuracy in their balance mode, which is in the range of commercial offerings.</p>
<p>You should still verify on your own traffic, all WAF benchmarks have a strong &ldquo;tested on the dataset we built&rdquo; flavour, but in practice the false-positive rate on our test workloads has been substantially lower than ModSecurity with CRS at paranoia level 2. That alone changes the operational story: a WAF you do not have to constantly whitelist around is a WAF that stays on.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<p>The community edition (which is what we are talking about, the Pro edition is announced but not generally available at the time of writing) ships with:</p>
<ul>
<li><strong>Web attack detection</strong> for the OWASP-canonical categories: SQL injection, XSS, command and code injection, XXE, SSRF, path traversal, deserialization patterns.</li>
<li><strong>Rate limiting</strong> per IP, per path, per session, with configurable windows and burst behaviour.</li>
<li><strong>Anti-bot challenge</strong>, a JavaScript proof-of-work that legitimate browsers solve transparently and headless scrapers stumble on.</li>
<li><strong>Authentication challenge</strong>, a separate gate you can put in front of admin panels (think: a captcha-like step before reaching <code>/wp-admin/</code>).</li>
<li><strong>Dynamic HTML/JS encryption</strong>, which mangles client-side identifiers per request so scrapers and credential-stuffing tools cannot easily target form fields.</li>
<li><strong>Geo blocking</strong> by country, useful for sites with no business outside a specific region.</li>
<li><strong>Logging and analytics dashboard</strong>, which is genuinely useful (more on this below).</li>
</ul>
<p>What it does not do: it is not a CDN. It does not cache static assets, does not give you POPs around the world, does not protect against L3/L4 volumetric DDoS at the network edge. SafeLine is a layer-7 application firewall. If you are taking 100 Gbit of UDP reflection, you need network-layer mitigation upstream, the kind we run at AS57050 with <a href="/en/network/">our own RTBH and sFlow setup</a>
, before the traffic ever reaches your VPS.</p>
<h2 id="installing-safeline-on-a-vps">Installing SafeLine on a VPS</h2>
<p>This is the part where most &ldquo;open source WAF&rdquo; projects make you regret the decision. SafeLine genuinely does not. The official one-liner does the right thing on a clean Debian or Ubuntu box.</p>
<p>Minimum sane resources: <strong>2 vCPU, 4 GB RAM, 10 GB free disk</strong>. The script will warn under 5 GB free. SSSE3 CPU instructions are required (any x86 CPU made in the last 15 years has them, but if you are running on something exotic, check <code>/proc/cpuinfo</code>). Both x86_64 and arm64 are supported.</p>
<p>On a fresh server:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bash -c <span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>curl -fsSLk https://waf.chaitin.com/release/latest/setup.sh<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><p>The script will:</p>
<ol>
<li>Install Docker and the Compose plugin if they are missing.</li>
<li>Drop a docker-compose stack at <code>/data/safeline/</code>.</li>
<li>Pick an unused private subnet (it tries <code>172.22.222.0/24</code>, <code>169.254.222.0/24</code>, <code>192.168.222.0/24</code> in order).</li>
<li>Generate a random 32-char Postgres password.</li>
<li>Pull and start four containers: <code>safeline-pg</code>, <code>safeline-mgt</code>, <code>safeline-detector</code>, <code>safeline-tengine</code>.</li>
</ol>
<p>Once it finishes, the management UI is at <code>https://&lt;your-server-ip&gt;:9443/</code> with a self-signed certificate. To grab the initial admin credentials:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker exec safeline-mgt resetadmin
</span></span></code></pre></div><p>That command prints a one-time username and password you can use for the first login. Change the password immediately after you are in. While you are at it, restrict port 9443 with a firewall rule, the management plane should not be exposed to the internet:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow from YOUR_OFFICE_IP to any port <span style="color:#ae81ff">9443</span>
</span></span><span style="display:flex;"><span>ufw deny <span style="color:#ae81ff">9443</span>
</span></span></code></pre></div><p>If you are running this on a <a href="/en/virtual-servers/vps/">DreamServer VPS</a>
, the same rule belongs in your provider&rsquo;s network firewall too, do not rely on host-level rules alone for management interfaces.</p>
<h2 id="putting-a-real-site-behind-it">Putting a real site behind it</h2>
<p>Adding a protected site is straightforward but the mental model takes a minute to click. SafeLine becomes the public-facing endpoint; your real application becomes an internal upstream that only SafeLine talks to.</p>
<p>In the management UI, <strong>Sites</strong> &gt; <strong>Add site</strong>:</p>
<ul>
<li><strong>Domain</strong>: <code>example.com</code> (and any aliases you serve).</li>
<li><strong>Port</strong>: <code>80</code> for plain HTTP, <code>443</code> for HTTPS, both if you want SafeLine to do the redirect.</li>
<li><strong>Upstream</strong>: the IP and port of your origin, for example <code>10.0.0.5:8080</code>. If your origin lives on the same box, use the docker bridge gateway (typically <code>172.17.0.1</code> or whatever the script picked) and the port your application listens on.</li>
<li><strong>TLS certificate</strong>: paste a cert and key, or upload a Let&rsquo;s Encrypt-issued one. SafeLine does not currently auto-issue certificates, you bring your own.</li>
</ul>
<p>Save, then point your domain&rsquo;s A/AAAA records at the SafeLine host. Within a few seconds the site shows up under <strong>Detection logs</strong> and you can watch live traffic flow through.</p>
<p>The first thing to verify is that SafeLine is genuinely inline. Send a deliberately bad request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -k <span style="color:#e6db74">&#34;https://example.com/?id=1&#39; OR &#39;1&#39;=&#39;1&#34;</span>
</span></span></code></pre></div><p>You should get a SafeLine block page (HTML body, HTTP 403 by default) and a corresponding entry in the detection log. If the request goes through to your application, your DNS or upstream config is wrong, fix that before you trust anything else.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<p>A few patterns we have found genuinely useful beyond the defaults.</p>
<h3 id="whitelisting-health-checks">Whitelisting health checks</h3>
<p>If your monitoring system pings <code>/health</code> from a known IP, do not let SafeLine score those requests. Add an <strong>Allow rule</strong> at the top of the policy chain matching source IP and path, with action &ldquo;skip detection&rdquo;. The point is not to save CPU, it is to keep your detection logs uncluttered with traffic you trust.</p>
<h3 id="rate-limit-before-block">Rate limit before block</h3>
<p>When you turn on rate limiting for a login endpoint, prefer <strong>challenge</strong> over <strong>block</strong> for the first threshold. A bot that runs into a challenge silently dies; a human who fat-fingered their password three times in a row gets through. Reserve the block action for the second threshold (say, 30 requests per 5 minutes from a single IP).</p>
<h3 id="anti-bot-for-the-entire-site-not-just-login">Anti-bot for the entire site, not just login</h3>
<p>Counter-intuitive but useful: turn on the JS challenge sitewide for low-value paths (the homepage, the blog) on a small percentage of traffic. The bots that fail the challenge there are the same ones that would later try your login form. You learn a lot about what is hitting you before any sensitive endpoint sees the traffic.</p>
<h3 id="authentication-challenge-for-admin-paths">Authentication challenge for admin paths</h3>
<p>For anything matching <code>/admin*</code>, <code>/wp-admin*</code>, <code>/phpmyadmin*</code>, add an authentication challenge layer. SafeLine implements this as an extra gate that requires solving a CAPTCHA-style challenge before the request reaches your application. It does not replace your application&rsquo;s auth, it just stops automated tools from ever knocking on the door.</p>
<h3 id="read-the-logs">Read the logs</h3>
<p>The dashboard at <strong>Statistics</strong> &gt; <strong>Detection logs</strong> shows every blocked request with the parsed reason: which engine triggered (semantic SQLi, regex XSS, anti-bot), the request body, the score. Spend an hour going through it the day after you turn SafeLine on. You will find that most of your &ldquo;weird traffic&rdquo; looks the same: a handful of mass-scanning toolkits hitting the same WordPress admin paths and the same exposed <code>.env</code> URLs.</p>
<h2 id="where-safeline-is-not-the-right-answer">Where SafeLine is not the right answer</h2>
<p>It is worth being honest about the limits.</p>
<ul>
<li><strong>Not a CDN.</strong> No caching, no edge POPs. If your performance story depends on edge proximity, you still need a CDN in front of (or beside) SafeLine.</li>
<li><strong>Not for sub-millisecond paths.</strong> Every request goes through detection. Median overhead in our tests was 1 to 3 ms on warm caches, which is fine for almost any web app, but if you are running an HFT API, this is not your tool.</li>
<li><strong>Pro features are not all open.</strong> Some of the more sophisticated dynamic-protection features and the multi-node clustering are clearly aimed at the upcoming Pro edition. The community edition is generous, but if you are protecting a fleet of dozens of sites with HA requirements, plan for that.</li>
<li><strong>Self-hosting is not free.</strong> You are running another stateful service that needs backups, OS patching, and the occasional Postgres babysitting. If your team does not want that, a managed WAF will be cheaper in operator hours.</li>
</ul>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p>For completeness: we also looked at <a href="https://github.com/bunkerity/bunkerweb" rel="nofollow">BunkerWeb</a>
 (nginx-based, very approachable, weaker semantic detection), <a href="https://coraza.io/" rel="nofollow">Coraza</a>
 (a Go-native ModSecurity reimplementation, great if you want to embed WAF logic directly in a service mesh), and the OWASP CRS on a stock nginx with <code>ngx_security_module</code> (battle-tested, infinitely tunable, false positives are your hobby now).</p>
<p>SafeLine ended up being the cleanest fit for &ldquo;I want a real WAF in front of a small fleet of self-hosted sites without becoming a WAF specialist&rdquo;. Your weights might be different.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you self-host PHP, Node, Python or Go web applications on a VPS or dedicated server and you have been meaning to &ldquo;do something about security headers and bot traffic&rdquo;, SafeLine is worth a Sunday afternoon. The install is genuinely fast, the detection works without you fighting it, and the dashboard tells you something true about your traffic.</p>
<p>If you are running it in production, put it on a server with a real backup story (Proxmox Backup, snapshots, your call) and treat the management plane as critical infrastructure. The detection containers can be rebuilt in a minute; the configuration in Postgres is what you would actually miss.</p>
<p>We run SafeLine ourselves in front of a couple of internal services and recommend it to customers who ask. If you want to try it on infrastructure you do not have to build first, our <a href="/en/virtual-servers/vps/">VPS plans</a>
 start at small enough sizes to make the experiment cheap, and our <a href="/en/services/server-administration/">server administration service</a>
 covers the install and tuning if you would rather skip the learning curve.</p>
<p>Either way, kicking the tires on SafeLine is a better way to spend an evening than reading another &ldquo;top 10 WAF&rdquo; listicle. The code is on GitHub, the install is one command, and you will know within an hour whether it earns a spot in your stack.</p>
]]></content:encoded><category>WAF</category><category>Security</category></item><item><title>Uptime Kuma: uptime monitoring without the headache</title><link>https://dreamserver.ro/en/blog/uptime-kuma-uptime-monitoring/</link><pubDate>Sat, 16 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/uptime-kuma-uptime-monitoring/</guid><description>What Uptime Kuma is, what it monitors out of the box, what a public status page looks like, and how to install it on a VPS in a few minutes.</description><content:encoded><![CDATA[<p>You don&rsquo;t need an enterprise monitoring stack to find out your site went down before a customer does. You need something that periodically checks a URL, sends you a message when the response isn&rsquo;t what you expect, and shows you a simple page with the uptime history. Uptime Kuma does exactly that, with an interface that looks better than most paid tools in its category.</p>
<p>The project lives at <a href="https://github.com/louislam/uptime-kuma" rel="nofollow">github.com/louislam/uptime-kuma</a>
, under the MIT license, created and maintained by Louis Lam. We run <a href="/en/blog/grafana-open-source-monitoring-dashboards/">Gatus</a>
 for our own status monitoring, because we prefer config-as-code, but Uptime Kuma is a very good alternative if you want an experience driven entirely from the web UI, without writing a single line of YAML.</p>
<h2 id="what-uptime-kuma-actually-is">What Uptime Kuma actually is</h2>
<p>Uptime Kuma is essentially the self-hosted version of services like UptimeRobot or Pingdom: you add a monitor, choose what to check and how often, and the app shows you response time, uptime percentage, and sends you notifications when the state changes.</p>
<p>Technically, it&rsquo;s a Node.js app, runs in a single container, and stores everything in SQLite, no separate database needed. The frontend is built in Vue and talks live to the backend via socket.io, so the dashboard updates in real time without a page refresh, which gives it the feel of a modern app rather than an old-school sysadmin tool.</p>
<p>The monitor types covered out of the box are surprisingly broad: HTTP(S) with status code or keyword checks on the response, TCP port, ping, DNS records, Docker containers (checked directly through the socket), gRPC, and push-type monitors, where an external job (a cron, a backup script) sends a &ldquo;heartbeat&rdquo; to Uptime Kuma instead of being polled by it.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>For a solo developer or a small team, a monitoring stack you never fully configure helps nobody. Uptime Kuma gives you useful observability in five minutes: install it, add a few URLs, set up a Telegram notification, done. You don&rsquo;t need templates, discovery, or complex triggers, just the answer to &ldquo;is it up or not&rdquo;.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>A reactive dashboard</strong>, with live websocket updates, no manual page refresh needed.</li>
<li><strong>Status pages</strong>, public or private, customizable, with support for multiple pages for different audiences (one public page for customers, one internal for the team).</li>
<li><strong>Over 90 notification integrations</strong>: Telegram, Slack, Discord, email, generic webhook, PagerDuty and many more.</li>
<li><strong>Multi-user support and 2FA</strong> for access to the instance.</li>
<li><strong>TLS certificate expiry monitoring</strong>, with alerts a few days before expiration.</li>
<li><strong>Maintenance windows</strong>, so you don&rsquo;t get false alerts during planned maintenance.</li>
<li><strong>Simple configuration import/export</strong> and a basic API for custom integrations.</li>
</ul>
<h2 id="installing-uptime-kuma-on-a-vps">Installing Uptime Kuma on a VPS</h2>
<p>A single container, with a volume for persistent data:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker volume create uptime_kuma_data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker run -d --name uptime-kuma <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 3001:3001 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v uptime_kuma_data:/app/data <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  louislam/uptime-kuma:1
</span></span></code></pre></div><p>On first access at <code>http://&lt;vps-ip&gt;:3001</code>, you&rsquo;re asked to create the admin account. In production, put it behind a reverse proxy with TLS, Uptime Kuma doesn&rsquo;t have its own certificate, and there&rsquo;s no reason to expose port 3001 directly to the internet.</p>
<p>With <code>ufw</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 3001/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>Resource needs are minimal: the container runs comfortably on the smallest <a href="/en/virtual-servers/vps/">VPS</a>
 we offer, even with a few dozen active monitors checking at one-minute intervals.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="push-monitors-for-internal-jobs">Push monitors for internal jobs</h3>
<p>Instead of waiting for Uptime Kuma to periodically check whether a cron job or backup script ran, you can set up a &ldquo;push&rdquo; monitor: the script itself sends an HTTP request to Uptime Kuma at the end of its run. If the request doesn&rsquo;t arrive within the expected interval, the monitor flips to &ldquo;down&rdquo;. It&rsquo;s the &ldquo;dead man&rsquo;s switch&rdquo; technique, useful for jobs that run infrequently (daily backup, weekly sync) where classic polling doesn&rsquo;t make sense.</p>
<h3 id="separate-status-pages-per-audience">Separate status pages per audience</h3>
<p>You can create a simple public status page with only the services relevant to customers, and a separate, internal page with all the technical detail for the operations team. Both live on the same instance, but show different lists of monitors.</p>
<h3 id="retries-and-flapping-resistance-thresholds">Retries and flapping resistance thresholds</h3>
<p>Each monitor has a configurable number of consecutive failed checks before it triggers an alert (&ldquo;retries&rdquo;), plus a resend interval if the problem persists. Set correctly, it eliminates false alerts for a two-second network blip while still notifying you quickly about a real problem.</p>
<h3 id="docker-monitor-type-for-small-self-hosted-stacks">Docker monitor type for small self-hosted stacks</h3>
<p>If you run a few containers on the same VPS, the Docker monitor type checks the container&rsquo;s state directly through the Docker socket, without making an HTTP request to it, useful for internal services with no exposed port.</p>
<h2 id="where-uptime-kuma-is-not-the-right-answer">Where Uptime Kuma is not the right answer</h2>
<p>All configuration lives in SQLite, managed through the web UI. If you want monitoring to be version-controlled in Git, reviewed through pull requests, and deployed identically across multiple environments, a config-as-code tool like Gatus fits better, we detailed that approach in <a href="/en/blog/grafana-open-source-monitoring-dashboards/">our monitoring stack article</a>
.</p>
<p>It&rsquo;s not a metrics platform: it gives you binary status (up/down) and latency, not detailed graphs of CPU, memory, or custom application metrics. For that you need VictoriaMetrics/Prometheus and Grafana.</p>
<p>Finally, it&rsquo;s a single-node instance with no built-in replication. If your public status page runs on the same server that hosts the monitored services, an incident that takes down the server also takes down your monitor, exactly when you need to know what&rsquo;s happening. For critical status pages, run the instance on infrastructure separate from what it monitors.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><strong>Gatus</strong> is our preferred alternative for config-as-code monitoring: everything is defined in YAML, version-controlled in Git, with no hidden state in a database, a good fit for teams who want the entire monitoring configuration in one place, reviewable as code.</p>
<p><strong>Checkmk or Zabbix</strong> are much heavier than you need for uptime monitoring alone, but worth considering if you end up also needing deeper infrastructure monitoring (CPU, memory, system services), not just external availability.</p>
<p><strong>Statping-ng or Cachet</strong> are other, older status page options, but with notification and maintenance ecosystems less active than Uptime Kuma&rsquo;s.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you need a quick answer to &ldquo;is my site up?&rdquo; plus a presentable status page for customers, and you want everything ready to go in five minutes without writing any configuration, Uptime Kuma is the obvious choice. When you grow past a few dozen monitors or want Git-versioned configuration, consider Gatus or a heavier infrastructure tool.</p>
<p>You can spin up the container above on a <a href="/en/virtual-servers/vps/">VPS</a>
 with us in a few minutes, or let our <a href="/en/services/server-administration/">server administration</a>
 team set up your monitoring and notifications from scratch.</p>
]]></content:encoded><category>Monitoring</category></item><item><title>SmokePing: seeing the jitter and packet loss a plain ping hides from you</title><link>https://dreamserver.ro/en/blog/smokeping-latency-monitoring/</link><pubDate>Wed, 13 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/smokeping-latency-monitoring/</guid><description>SmokePing measures and visualizes latency over time, with its signature smoke graph showing jitter and packet loss where a plain up/down check sees nothing.</description><content:encoded><![CDATA[<p>A user complains that &ldquo;the internet goes weird sometimes,&rdquo; games lag, video calls stutter. You check the uptime dashboard: 100%, all green, ping responds fine. The problem is a plain up/down check only tells you whether a host answered at a given moment, not what latency looked like over time - a network can be &ldquo;up&rdquo; the entire time and still have high jitter or intermittent packet loss that makes any latency-sensitive application (VoIP, gaming, video conferencing) feel broken, without a single classic alert ever firing.</p>
<p><a href="https://github.com/oetiker/SmokePing" rel="nofollow">SmokePing</a>
 (<a href="https://oss.oetiker.ch/smokeping/" rel="nofollow">oss.oetiker.ch/smokeping</a>
) is the tool built exactly for that gap, written by Tobi Oetiker, the same author behind RRDtool and MRTG - two tools half the network monitoring world uses without necessarily knowing who wrote them. GPL licensed, it&rsquo;s a classic Perl tool that never tried to be anything other than very good at one job: measuring latency repeatedly, over time, and displaying it in a way that makes problems obvious at a glance.</p>
<h2 id="what-smokeping-actually-is">What SmokePing actually is</h2>
<p>SmokePing periodically sends probes (FPing by default, but it also supports DNS, HTTP(S), SSH, TCP and many other protocols through its pluggable probe system) toward a list of targets and measures the response time for each attempt. Rather than keeping only an average, it retains the full distribution of measurements within an interval, and the result is stored in RRDtool, the same round-robin database also used by MRTG.</p>
<p>The project&rsquo;s visual signature - where the name comes from - is the &ldquo;smoke&rdquo; graph: for each time bucket, instead of a single average-latency line, you see a grey band, thicker or more diffuse depending on how spread out the individual measurements were. A thin, constant band means stable latency. A thick, &ldquo;smoky&rdquo; band means high jitter - large variance between packets, exactly what a user experiences as &ldquo;inconsistent lag.&rdquo; Packet loss overlays on the same graph via color (from green to red), so you can see three things at once, at a glance: average latency, its variance, and the percentage of lost packets.</p>
<p>For larger networks, SmokePing supports a master/slave setup: a central server coordinates multiple geographically distributed measurement nodes, each reporting back to the master. Useful when you need to know not just &ldquo;how far is X from me,&rdquo; but &ldquo;how far is X from several different points in the network.&rdquo;</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference from classic up/down monitoring is that SmokePing answers a different question. An uptime check tells you &ldquo;the server responded at time X.&rdquo; SmokePing tells you &ldquo;how did the network path to the server behave over the last hour, week, month&rdquo; - and that second question is the one that matters when you&rsquo;re trying to figure out why a customer complains about a bad experience even though all your classic alerts are silent. Many real network problems (an intermittently saturated link, a switch starting to drop packets before it fully dies, an unstable peering session) show up in jitter and partial loss well before they become a full outage.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li>A latency graph with a &ldquo;smoke&rdquo; band showing the full distribution of measurements, not just the average</li>
<li>Visual overlay of packet loss via color coding, on the same graph</li>
<li>Pluggable probes: FPing (default), DNS, HTTP(S), SSH, TCP, and many more through Perl modules</li>
<li>RRDtool storage, so compact historical graphs with decreasing resolution as data ages</li>
<li>Master/slave configuration for distributed measurements from multiple vantage points</li>
<li>Configurable alerting on latency or packet-loss thresholds</li>
<li>A web interface for interactive navigation through graphs and zooming into time ranges</li>
</ul>
<h2 id="installing-smokeping-on-a-vps">Installing SmokePing on a VPS</h2>
<p>SmokePing is a lightweight tool, well suited to a small dedicated monitoring <a href="/en/virtual-servers/vps/">VPS</a>
 - in fact, a vantage point separate from the infrastructure you&rsquo;re monitoring is ideal, so you see latency the way the outside world sees it, not only from inside your own network.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># on Debian/Ubuntu</span>
</span></span><span style="display:flex;"><span>apt update
</span></span><span style="display:flex;"><span>apt install smokeping fping apache2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># enable the CGI module and the Apache config shipped with the package</span>
</span></span><span style="display:flex;"><span>a2enmod cgi
</span></span><span style="display:flex;"><span>a2enconf smokeping-apache2
</span></span><span style="display:flex;"><span>systemctl reload apache2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># targets to monitor are defined in /etc/smokeping/config.d/Targets</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># minimal example to add under an existing section:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   + MyServer</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   menu = My Server</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   title = Latency to production server</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   host = 203.0.113.10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemctl restart smokeping
</span></span></code></pre></div><p>After install, the web interface is available by default at <code>/smokeping</code> under Apache, and the daemon starts accumulating data immediately - the first useful graphs appear after a few hours, but real trends (recurring jitter at certain hours, packet loss in a pattern) only become visible after a few days of collection.</p>
<pre tabindex="0"><code class="language-ufw" data-lang="ufw"># web interface (Apache), ideally restricted to your IP or a private network
ufw allow from &lt;your_IP&gt; to any port 443 proto tcp
# FPing needs the CAP_NET_RAW capability for ICMP, handled automatically
# by the Debian package - no extra open port required beyond this
</code></pre><p>If you&rsquo;re using a master/slave setup, slave nodes need connectivity to the master on whatever port you configure for the RPC channel between them (typically an SSH tunnel or a dedicated channel, depending on how you set it up).</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="pick-the-right-probe-for-what-you-actually-want-to-measure">Pick the right probe for what you actually want to measure</h3>
<p>FPing measures network-level latency (ICMP), but if you want to know how long a service actually takes to respond (not just whether the host is &ldquo;up&rdquo;), use protocol-specific probes: DNS for a resolver, HTTP(S) for a site, SSH for a server you connect to frequently. A server can answer ICMP perfectly while the web service running on it has high latency for entirely different reasons.</p>
<h3 id="rrdtool-resolution-decreases-over-time-by-design">RRDtool resolution decreases over time, by design</h3>
<p>RRDtool keeps high-resolution data for recent periods and progressively aggregates older periods, so files don&rsquo;t grow forever. That means if you want to investigate a specific event from three months ago at second-level granularity, you likely won&rsquo;t have it - plan your alerting thresholds and archiving around that, don&rsquo;t assume you have full history at maximum resolution indefinitely.</p>
<h3 id="use-masterslave-to-tell-my-problem-from-their-problem">Use master/slave to tell &ldquo;my problem&rdquo; from &ldquo;their problem&rdquo;</h3>
<p>With a single measurement point, you can&rsquo;t know whether a latency spike is caused by your local network, an intermediate ISP, or the destination itself. With slave nodes in different locations (a different datacenter, a different ISP), you can correlate: if all nodes see the same problem at the same time, the cause is likely at the destination or a shared transit point; if only one node sees it, the problem is local to that vantage point.</p>
<h3 id="alerts-deserve-thought-out-thresholds-not-defaults">Alerts deserve thought-out thresholds, not defaults</h3>
<p>SmokePing can send alerts on latency and packet-loss thresholds, but the defaults know nothing about your specific network. 150ms latency can be entirely normal for an intercontinental link and alarming for a local one. Set thresholds per target, not globally.</p>
<h2 id="where-smokeping-is-not-the-right-answer">Where SmokePing is not the right answer</h2>
<p>SmokePing measures network-level latency and loss, but it doesn&rsquo;t replace a full application or infrastructure monitoring system (CPU, memory, disk, processes). For that you need a separate stack (Prometheus/Grafana, Zabbix, Checkmk, or others), which SmokePing can complement, not replace.</p>
<p>Its interface is also functionally vintage: static, server-side-rendered RRDtool graphs, no modern interactive drag-and-drop dashboards. If you want cross-metric correlation in a single panel (latency + CPU + traffic on the same graph), you&rsquo;ll need to export the data or pair it with another tool.</p>
<p>Finally, for very large networks with hundreds of targets, configuration through text files and a hierarchical menu structure can become unwieldy compared to modern solutions with automatic discovery.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://github.com/prometheus/blackbox_exporter" rel="nofollow">Prometheus Blackbox Exporter + Grafana</a>
</strong>: a modern stack with flexible time-series storage and interactive dashboards, but it takes more pieces wired together to reach the same visual result</li>
<li><strong><a href="https://github.com/traviscross/mtr" rel="nofollow">MTR</a>
</strong>: combines traceroute with repeated per-hop latency measurements, excellent for point-in-time diagnosis, but not for continuous long-term monitoring</li>
<li><strong><a href="https://www.zabbix.com" rel="nofollow">Zabbix</a>
</strong>: a full monitoring platform that also includes ICMP latency measurements, with more sophisticated alerting, but a steeper learning curve and a much larger configuration surface</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;ve ever needed to answer &ldquo;was the connection really unstable yesterday between 2 and 4pm, or is the customer imagining it,&rdquo; SmokePing is exactly the tool that gives you the answer, with visual proof at hand. It&rsquo;s easy to install, light on resources, and its smoke graph remains, almost two decades after its debut, one of the clearest visual representations of jitter the open-source ecosystem has.</p>
<p>It runs comfortably on a small <a href="/en/virtual-servers/vps/">VPS</a>
, ideally placed outside the network you&rsquo;re monitoring, so you see latency from a real user&rsquo;s perspective. If you want to fold it into a broader monitoring strategy without handling server maintenance yourself, check out our <a href="/en/services/server-administration/">server administration</a>
 service or our <a href="/en/network/">network section</a>
 for context on how these measurements tie into the rest of an infrastructure.</p>
]]></content:encoded><category>Network</category><category>Monitoring</category></item><item><title>Zabbix: enterprise-scale monitoring without the license bill</title><link>https://dreamserver.ro/en/blog/zabbix-enterprise-monitoring/</link><pubDate>Sat, 09 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/zabbix-enterprise-monitoring/</guid><description>What Zabbix is, how it scales from 10 to 10,000 hosts through distributed proxies, what you get out of the box, and how to install it on a VPS.</description><content:encoded><![CDATA[<p>Most monitoring tools work fine up to a certain host count, and then either the monitoring server collapses under its own weight or the license bill explodes. Zabbix is built specifically to avoid both: it&rsquo;s designed from the ground up for large scale, and it&rsquo;s completely free regardless of how many hosts you monitor.</p>
<p>The source lives at <a href="https://github.com/zabbix/zabbix" rel="nofollow">github.com/zabbix/zabbix</a>
, under GPLv2. The company behind it, Zabbix SIA (Latvia), doesn&rsquo;t run a separate &ldquo;enterprise edition&rdquo; with features locked behind a paywall, everything is in the open source version, and revenue comes from support, training and certification. That&rsquo;s a rare approach in the monitoring market, where &ldquo;open source&rdquo; usually means &ldquo;the stripped-down version of the paid product&rdquo;.</p>
<h2 id="what-zabbix-actually-is">What Zabbix actually is</h2>
<p>Zabbix has existed since 1998, created by Alexei Vladishev, and has gone through several architectural generations to reach its current form. Main components: the <strong>Zabbix server</strong> (the central engine, processes data and evaluates triggers), an optional <strong>Zabbix proxy</strong> (collects data locally in a segmented network and forwards it compressed to the server), <strong>agents</strong> (Zabbix Agent 2, rewritten in Go, active or passive) and a PHP <strong>frontend</strong> for administration and visualization. Historical data is stored in MySQL, PostgreSQL, or, for large volumes, PostgreSQL with the TimescaleDB extension.</p>
<p>The data model is simple once you see it: an <strong>item</strong> is a single collected metric (CPU load, free disk space, HTTP response time). A <strong>trigger</strong> is a logical expression over one or more items that flips a state between OK and Problem. An <strong>action</strong> defines what happens when a trigger fires: send a notification, run a remediation command, escalate after an interval if nobody acknowledges it. On top of all that sit <strong>templates</strong>: reusable bundles of items, triggers and graphs, attached to a host class (all Cisco switches, all MySQL servers) and inherited automatically.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>Zabbix&rsquo;s real differentiator is the proxy. When you have a segmented network, a remote site, an isolated VLAN, a secondary datacenter without direct connectivity to the central server, a Zabbix proxy runs locally, collects all the data there, and sends it compressed through a single tunnel to the server. The central server no longer has to open connections to thousands of individual agents; it only talks to proxies. That&rsquo;s what allows scaling from a few dozen to tens of thousands of monitored devices without redesigning the architecture.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Multiple collection channels</strong>: Zabbix Agent 2 (active or passive), SNMP, IPMI, JMX (through a Java gateway), an HTTP agent for webhooks and custom integrations, and calculated items derived from other items.</li>
<li><strong>Two-level auto-discovery</strong>: network discovery (scans an IP range and adds new hosts) and low-level discovery (LLD) for dynamic entities inside a host, filesystems, network interfaces, processes, that appear and disappear over time.</li>
<li><strong>A large template library</strong>, official and community-contributed, for operating systems, network gear from dozens of vendors, and common applications.</li>
<li><strong>Trigger dependencies</strong>, so you don&rsquo;t get 50 &ldquo;host down&rdquo; alerts when what actually failed is a single upstream switch.</li>
<li><strong>Multi-step escalation actions</strong>, with resends and different channels depending on how long a problem has gone unacknowledged.</li>
<li><strong>Network maps</strong> for topology visualization and configurable widget-based dashboards.</li>
<li><strong>Distributed monitoring</strong> through proxies, for segmented networks or very large fleets.</li>
</ul>
<h2 id="installing-zabbix-on-a-vps">Installing Zabbix on a VPS</h2>
<p>The cleanest way to try Zabbix is through Docker Compose, with a PostgreSQL backend:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">postgres</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16-alpine</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">zabbix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">change_me</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">zabbix</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">pgdata:/var/lib/postgresql/data</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">zabbix-server</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">zabbix/zabbix-server-pgsql:6.4-alpine-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DB_SERVER_HOST</span>: <span style="color:#ae81ff">postgres</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">zabbix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">change_me</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">depends_on</span>: [<span style="color:#ae81ff">postgres]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">zabbix-web</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">zabbix/zabbix-web-nginx-pgsql:6.4-alpine-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DB_SERVER_HOST</span>: <span style="color:#ae81ff">postgres</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">zabbix</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">change_me</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ZBX_SERVER_HOST</span>: <span style="color:#ae81ff">zabbix-server</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;8080:8080&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">depends_on</span>: [<span style="color:#ae81ff">zabbix-server]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">zabbix-agent</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">zabbix/zabbix-agent2:6.4-alpine-latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ZBX_HOSTNAME</span>: <span style="color:#ae81ff">vps-monitor</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ZBX_SERVER_HOST</span>: <span style="color:#ae81ff">zabbix-server</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">pgdata</span>:
</span></span></code></pre></div><p><code>docker compose up -d</code> starts the whole stack. The frontend is on port 8080, and the default login is <code>Admin</code> / <code>zabbix</code>, change it immediately.</p>
<p>For the firewall, with <code>ufw</code>, expose only what needs to be public (ideally nothing, put the frontend behind a reverse proxy):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 8080/tcp
</span></span><span style="display:flex;"><span>ufw deny 10050/tcp   <span style="color:#75715e"># agent port, restricted to the Zabbix server IP only</span>
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>Port 10050 (passive agent) should only be reachable from the Zabbix server&rsquo;s or proxy&rsquo;s IP, not the open internet, <code>ufw allow from &lt;server-ip&gt; to any port 10050</code>. A <a href="/en/virtual-servers/vps/">VPS</a>
 with 2 GB RAM comfortably runs the server plus frontend for a few hundred hosts; for thousands of hosts, add separate proxies.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="proxies-for-segmented-networks">Proxies for segmented networks</h3>
<p>If you have a branch office, an isolated VLAN, or a site without direct connectivity to the central server, a local Zabbix proxy solves it without a complicated VPN: the proxy collects everything locally and sends a single compressed stream to the server. It&rsquo;s also easier on the firewall, one open port instead of hundreds of individual connections.</p>
<h3 id="low-level-discovery-for-dynamic-entities">Low-level discovery for dynamic entities</h3>
<p>Instead of manually configuring monitoring for every disk or network interface, LLD periodically scans the host and creates/removes items automatically based on what it finds. When you add a new disk to a server, LLD picks it up on the next run, with no configuration change on your part.</p>
<h3 id="trigger-dependencies-to-cut-the-noise">Trigger dependencies to cut the noise</h3>
<p>When an access switch goes down, without dependencies configured you get an alert for every host behind it. With a dependency set (hosts depend on the switch&rsquo;s availability), Zabbix suppresses the derived alerts and shows you a single real problem: the switch.</p>
<h3 id="timescaledb-for-history-compression-at-scale">TimescaleDB for history compression at scale</h3>
<p>If you&rsquo;re storing data from thousands of items over the long term, plain PostgreSQL starts to struggle on space and query performance. The TimescaleDB extension, natively supported by Zabbix, automatically compresses older data and speeds up queries over large ranges.</p>
<h2 id="where-zabbix-is-not-the-right-answer">Where Zabbix is not the right answer</h2>
<p>For a homelab or 3-5 VPS instances, Zabbix is clearly overkill. The learning curve (items, triggers, actions, templates, proxies) doesn&rsquo;t pay off for a small fleet; there, an <a href="/en/blog/grafana-open-source-monitoring-dashboards/">Uptime Kuma or a Gatus</a>
 do the job in a few minutes.</p>
<p>The UI is functional, but looks and feels &ldquo;classic enterprise&rdquo;, you don&rsquo;t get the free-form exploration experience Grafana gives you over time-series data. Many teams use Zabbix as a data source, through the official Grafana plugin, precisely to combine Zabbix&rsquo;s solid collection/alerting engine with modern visual exploration.</p>
<p>It&rsquo;s also not ideal for ephemeral cloud-native workloads, where containers appear and disappear within seconds. Its discovery model assumes a host exists long enough to be discovered, configured and monitored; for environments where everything is ephemeral, a Prometheus-style model (pull with service discovery built into the orchestrator) fits more naturally.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><strong>Checkmk</strong> offers a more automated discovery flow out of the gate and a stronger ITIL focus, in exchange for slightly less flexibility in the data model.</p>
<p><strong>Prometheus/VictoriaMetrics + Grafana</strong> remain our choice for visual correlation and ad-hoc queries over metrics, we wrote about <a href="/en/blog/grafana-open-source-monitoring-dashboards/">why we use them</a>
 in a dedicated article.</p>
<p><strong>Nagios/Icinga</strong> remain relevant for teams that want granular ground-up control, but without the native proxy-based scalability Zabbix has.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you administer a large infrastructure, with lots of network gear, branch offices or segmented VLANs, and you want a mature, free system proven at enterprise scale, Zabbix is a solid choice. If your fleet is small, start with something simpler and come back to Zabbix when you grow.</p>
<p>You can start the test on a <a href="/en/virtual-servers/vps/">VPS</a>
 with us in a few minutes using the stack above, or let our <a href="/en/services/server-administration/">server administration</a>
 team handle installation, proxies and templates from scratch.</p>
]]></content:encoded><category>Monitoring</category></item><item><title>Checkmk: infrastructure monitoring that configures itself</title><link>https://dreamserver.ro/en/blog/checkmk-infrastructure-monitoring/</link><pubDate>Wed, 06 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/checkmk-infrastructure-monitoring/</guid><description>What Checkmk is, why its auto-discovery beats hand-written Nagios configs, what you get out of the box, and how to install it on a VPS. We run it ourselves.</description><content:encoded><![CDATA[<p>A sysadmin who hand-maintains monitoring config for 150 servers eventually ends up in one of two states: either they ignore half the alerts because they&rsquo;re noise, or they hear about a problem from a customer before it shows up on the dashboard. Both are symptoms of the same disease: someone had to write every check, for every host, by hand, and nobody kept up as the infrastructure grew.</p>
<p>Checkmk attacks exactly that problem. Instead of starting from a blank slate and manually declaring &ldquo;monitor the /var filesystem on host X&rdquo;, you point it at a host and it tells you what it found: filesystems, network interfaces, processes, systemd units, Docker containers, whatever is there. The code lives at <a href="https://github.com/Checkmk/checkmk" rel="nofollow">github.com/Checkmk/checkmk</a>
, the Raw edition is open source under GPLv2, and we run it in production ourselves, alongside Grafana and VictoriaMetrics, for service auto-discovery and classic host monitoring in our Bucharest datacenter.</p>
<h2 id="what-checkmk-actually-is">What Checkmk actually is</h2>
<p>Checkmk started around 2008 as a plugin called <code>check_mk</code> for Nagios, built to solve the pain of manually configuring checks. Over time the project grew into a full monitoring platform with its own collection engine and its own web interface, but it kept the original philosophy: discover first, configure second.</p>
<p>In practice it works like this: you install the Checkmk agent on a host (a small binary, no heavy dependencies) or configure it to talk SNMP/IPMI/HTTP to a device that can&rsquo;t run an agent. Then you run discovery, and Checkmk proposes a list of services to monitor based on what it actually found there. You accept the list (or tweak it), and from that point on the host is monitored.</p>
<p>There are two major edition tracks: <strong>Raw</strong>, fully open source, with a check engine historically based on Nagios-core, and the commercial <strong>Enterprise/Cloud</strong> editions, which bring a proprietary core (CMC), higher scale, reporting, and a few extra integrations (agent bakery, for example). For most self-hosted infrastructures, Raw is enough.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference versus classic Nagios or Zabbix isn&rsquo;t philosophical, it&rsquo;s practical: time. With 5 servers you write the config by hand and never notice the cost. With 50 or 500, every new host, every new disk, every reconfigured network interface means a manual edit that someone eventually forgets. Checkmk removes most of that repetitive work: you run discovery periodically, and the differences (new services that appeared, services that vanished) are flagged explicitly, not silently ignored.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Auto-discovery of hosts and services</strong>, backed by over 2000 bundled check plugins covering operating systems, network gear from dozens of vendors, databases, hypervisors and common applications.</li>
<li><strong>Hybrid collection</strong>: agent-based wherever you can install a binary, agentless via SNMP, HTTP/API or IPMI wherever you can&rsquo;t (switches, UPS units, BMCs).</li>
<li><strong>Rule-based notifications</strong>, with flexible routing to email, Slack, an SMS gateway or any custom script, depending on host, severity, or time of day.</li>
<li><strong>Downtimes and acknowledgements</strong>, plus an ITIL-style workflow for problem states (open, acknowledged, closed) and an event console for correlating syslog/SNMP traps.</li>
<li><strong>Hardware/software inventory</strong>, with change history, useful when you need to know what changed on a server between two incidents.</li>
<li><strong>Built-in graphs and dashboards</strong> based on RRD, plus a business intelligence feature that rolls up the state of multiple services into a single &ldquo;is this business service OK or not&rdquo; view.</li>
</ul>
<h2 id="installing-checkmk-on-a-vps">Installing Checkmk on a VPS</h2>
<p>The easiest way to try Checkmk is the official Docker image, which bundles a complete &ldquo;site&rdquo; (agent, core, UI, RRD store) into a single container:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker volume create checkmk_data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker run -d --name checkmk <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 8080:5000 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v checkmk_data:/omd/sites <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --tmpfs /opt/omd/sites/cmk/tmp:exec <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  checkmk/check-mk-raw:2.3.0-latest
</span></span></code></pre></div><p>On first start, the container generates an admin password for the <code>cmk</code> site and prints it to the log (<code>docker logs checkmk</code>). The UI is available at <code>http://&lt;vps-ip&gt;:8080/cmk/check_mk/</code>, but in production you put it behind a reverse proxy with TLS (Traefik, nginx, Caddy), you don&rsquo;t expose it directly.</p>
<p>For the firewall, with <code>ufw</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 22/tcp
</span></span><span style="display:flex;"><span>ufw allow 80/tcp
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span>ufw deny 8080/tcp
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><p>The idea is that port 8080 (or whatever internal app port) is only reachable from localhost/reverse proxy, not directly from the internet. For a small-to-medium fleet, a <a href="/en/virtual-servers/vps/">VPS</a>
 with 2-4 GB RAM is enough for discovery and monitoring up to a few dozen hosts; beyond that, allocate more memory or consider multiple distributed Checkmk sites.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="discovery-per-folder-not-per-host">Discovery per folder, not per host</h3>
<p>Checkmk organizes hosts into folders, and rules (what to monitor, how to notify, what thresholds to use) can be attached at the folder level, inherited by every host in it. Instead of configuring each server individually, you group similar hosts (all web servers, all Cisco switches) into a folder and apply rules once. Discovery runs periodically per folder, and differences from the known state show up as &ldquo;new/vanished services to review&rdquo;, not applied silently in the background.</p>
<h3 id="piggyback-data-for-indirect-architectures">Piggyback data for indirect architectures</h3>
<p>A useful technique when you have a machine (a hypervisor, a load balancer) that &ldquo;knows&rdquo; more about other hosts than they can learn on their own: Checkmk lets one host push monitoring data on behalf of another host (&ldquo;piggyback&rdquo;). A hypervisor can report VM state without each VM needing its own agent installed. The pattern is similar to how we auto-derive Prometheus/VictoriaMetrics scrape targets from our Ansible inventory: the source of truth stays in one place, and everything else is derived from it.</p>
<h3 id="event-console-for-correlating-syslog-and-snmp-traps">Event console for correlating syslog and SNMP traps</h3>
<p>If you&rsquo;re already receiving syslog or SNMP traps from network gear, Checkmk&rsquo;s event console can ingest and correlate them with existing hosts, turning a raw log stream into a structured monitoring signal, with matching rules and escalation.</p>
<h3 id="layered-notification-rules">Layered notification rules</h3>
<p>Notification rules can chain: a general rule for the whole infrastructure, overridden by specific rules for a group of critical hosts (production databases, for example) that go through a different channel with a different severity threshold. Avoid having a single Slack channel receiving everything, from a disk at 80% to a downed server.</p>
<h2 id="where-checkmk-is-not-the-right-answer">Where Checkmk is not the right answer</h2>
<p>If what you want is a correlated, ad-hoc view over high-cardinality metrics (per-request, per-container, per-API-endpoint), Checkmk is not the right tool; that&rsquo;s where Prometheus or VictoriaMetrics, queried through PromQL and visualized in Grafana, are far more flexible. We wrote about <a href="/en/blog/grafana-open-source-monitoring-dashboards/">Grafana as a visualization layer</a>
 in an earlier article.</p>
<p>It&rsquo;s not a log aggregator either: Checkmk&rsquo;s log monitoring is useful for simple pattern matching (search for a string, count occurrences), not full-text search over terabytes of logs, where you need Loki or ELK.</p>
<p>Finally, the Raw edition has real limits versus Enterprise: agent bakery (custom agent packaging per host group) and scaling past a few thousand hosts are why larger teams pay for the commercial edition. For a small-to-medium self-hosted fleet, though, Raw comfortably covers the need.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<p><strong>Zabbix</strong> is the most direct alternative: equally mature, equally open source, but with a different organizational model (items/triggers/actions instead of automatic service discovery) and a stronger focus on scaling through distributed proxies.</p>
<p><strong>Prometheus/VictoriaMetrics + Grafana</strong> remain our choice for time-series metrics and custom dashboards, we detailed <a href="/en/blog/grafana-open-source-monitoring-dashboards/">why we picked that combo</a>
 separately. Checkmk and this combo aren&rsquo;t mutually exclusive: many teams run them side by side, Checkmk for discovery + alerting + ITIL workflow, Grafana for visual correlation and exploration.</p>
<p><strong>Nagios</strong>, the direct ancestor, remains a valid option if you want full ground-up control, but the manual maintenance cost is exactly what Checkmk is trying to eliminate.</p>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you administer infrastructure with dozens or hundreds of heterogeneous hosts and network devices, and you want to stop hand-writing monitoring configs for each one, Checkmk is worth installing at least as a weekend proof of concept. Discovery alone, showing you within minutes what services run on a new host, justifies the time invested.</p>
<p>If you want to try it without dealing with installation and maintenance, <a href="/en/virtual-servers/vps/">get a VPS</a>
 with us and spin up the container above in a few minutes, or let us handle all of it through our <a href="/en/services/server-administration/">server administration service</a>
, from install to tuning the notification rules.</p>
]]></content:encoded><category>Monitoring</category></item><item><title>ZeroTier: when you need a virtual Ethernet switch, not just IP routing</title><link>https://dreamserver.ro/en/blog/zerotier-software-defined-network/</link><pubDate>Sat, 02 May 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/zerotier-software-defined-network/</guid><description>ZeroTier emulates a Layer 2 Ethernet switch over the internet, with broadcast and multicast, where WireGuard's strict Layer 3 model falls short.</description><content:encoded><![CDATA[<p>You need to connect two local networks together so a device on one can discover another via local network discovery (mDNS, UPnP, a NAS announcing itself via broadcast), not just ping it by IP. You try a classic Layer 3 VPN and find out those protocols simply don&rsquo;t traverse a regular IP tunnel, because they depend on sitting on the same Ethernet segment. That&rsquo;s where the ZeroTier conversation starts.</p>
<p><a href="https://www.zerotier.com/" rel="nofollow">ZeroTier</a>
 is a software-defined networking (SDN) platform, open source for the client and controller side (BSL 1.1 license), with code on <a href="https://github.com/zerotier/ZeroTierOne" rel="nofollow">GitHub</a>
. Unlike WireGuard or Tailscale, ZeroTier doesn&rsquo;t use the WireGuard protocol: it has its own encrypted protocol, built from scratch for a different purpose, to emulate a full Ethernet switch, not just a point-to-point IP tunnel. We evaluated it as an option for scenarios where Layer 2 matters; in production we run plain WireGuard.</p>
<h2 id="what-zerotier-actually-is">What ZeroTier actually is</h2>
<p>A ZeroTier network is identified by a 16-digit hexadecimal ID and behaves, from the perspective of connected devices, as a single virtual Ethernet segment, regardless of how many continents physically separate its members. Every device gets a virtual interface that participates in this segment, with full support for broadcast and multicast, exactly like on a physical switch.</p>
<p>Peer discovery happens through global root servers, called &ldquo;planets,&rdquo; operated by ZeroTier as public bootstrap infrastructure. On top of that, you can add your own &ldquo;moons,&rdquo; self-hosted relay/discovery servers, for cases where you want more control or redundancy independent of ZeroTier&rsquo;s infrastructure. The network controller, the one that decides who&rsquo;s part of the network and what rules apply, can also be self-hosted.</p>
<h3 id="why-this-approach-matters">Why this approach matters</h3>
<p>The Layer 3 model that WireGuard and Tailscale use is simple and efficient for most cases: IP routing between distinct networks. But there&rsquo;s an entire class of applications that assume they&rsquo;re sitting on the same physical network segment: device discovery via UDP broadcast, legacy industrial protocols, LAN games looking for servers via broadcast, management systems that rely on ARP and multicast to find each other. None of that works through a pure Layer 3 tunnel, no matter how well encrypted it is.</p>
<p>ZeroTier solves exactly this problem: by emulating Layer 2, any protocol that would work on a local network also works on the ZeroTier virtual network, even if members are spread across different servers in different countries. It&rsquo;s the difference between &ldquo;I have a connection to that server&rdquo; and &ldquo;that server is, from every software perspective, in the same room as me.&rdquo;</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Full Layer 2 emulation</strong>: working broadcast and multicast over the internet, not just unicast routing.</li>
<li><strong>16-digit network ID</strong>: a network is identified and administered through a single identifier, easy to distribute to new members.</li>
<li><strong>Rules engine / flow rules</strong>: a rules language that filters traffic inside the virtual network (equivalent to an internal firewall on the emulated segment).</li>
<li><strong>Global root servers (&ldquo;planets&rdquo;) + your own moons</strong>: peer discovery without depending 100% on public infrastructure, if you choose to host your own relays.</li>
<li><strong>Self-hostable network controller</strong>: you can run your own server that decides membership, without depending on the ZeroTier Central SaaS console.</li>
<li><strong>Clients on every major platform</strong>, including routers and embedded devices.</li>
</ul>
<h2 id="installing-zerotier-on-a-vps">Installing ZeroTier on a VPS</h2>
<p>Installing the standard client is straightforward on any Linux distro:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s https://install.zerotier.com | sudo bash
</span></span><span style="display:flex;"><span>sudo zerotier-cli join &lt;network-id&gt;
</span></span></code></pre></div><p>After joining, the member shows up in the network&rsquo;s admin list (either in ZeroTier Central or your self-hosted controller) and must be manually authorized before it can pass real traffic, a simple but essential security step.</p>
<p>For a self-hosted network controller on your own VPS (instead of using ZeroTier Central), you run the <code>ztncui</code> service or a similar controller on top of <code>zerotier-one</code> in controller mode:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name zerotier-controller <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --net<span style="color:#f92672">=</span>host <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --device<span style="color:#f92672">=</span>/dev/net/tun <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --cap-add NET_ADMIN --cap-add SYS_ADMIN <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v zerotier-data:/var/lib/zerotier-one <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  zyclonite/zerotier
</span></span></code></pre></div><p>The admin interface (either ztncui or the local API on port 9993) should never be exposed publicly. Lock it down:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw deny 9993/tcp
</span></span><span style="display:flex;"><span>ufw allow from &lt;your-ip&gt; to any port <span style="color:#ae81ff">9993</span> proto tcp
</span></span><span style="display:flex;"><span>ufw allow 9993/udp
</span></span></code></pre></div><p>You need a VPS with TUN/TAP support and access to the necessary kernel modules; check our <a href="/en/virtual-servers/vps/">VPS plans</a>
 for a clean base to run it on.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="manual-authorization-is-not-optional">Manual authorization is not optional</h3>
<p>Never leave a ZeroTier network on auto-authorize in production. Every new device must be explicitly approved in the admin console; otherwise, anyone who finds the network ID (which isn&rsquo;t a strong cryptographic secret) can request to join.</p>
<h3 id="use-flow-rules-not-just-membership">Use flow rules, not just membership</h3>
<p>Being a member of the network doesn&rsquo;t mean a device should see all traffic. Write explicit rules (ZeroTier&rsquo;s flow rules language) for internal segmentation, exactly like you&rsquo;d do with VLANs on a physical network.</p>
<h3 id="self-host-the-controller-if-security-requires-it">Self-host the controller if security requires it</h3>
<p>If your internal policy doesn&rsquo;t allow network membership to be decided by an external SaaS service, run your own controller. It&rsquo;s officially documented and supported, not a hack.</p>
<h3 id="your-own-moons-for-latency-and-independence">Your own moons for latency and independence</h3>
<p>If discovery through public &ldquo;planets&rdquo; adds extra latency, or you want a bootstrap path independent of ZeroTier&rsquo;s infrastructure, host your own &ldquo;moons&rdquo; on servers you control.</p>
<h2 id="where-zerotier-is-not-the-right-answer">Where ZeroTier is not the right answer</h2>
<p>If all you need is simple routing between two servers or point-to-point administrative access, the complexity of an emulated Layer 2 network isn&rsquo;t justified: WireGuard does the job more simply and faster. At large scale (thousands of devices), emulated broadcast and multicast over the internet can themselves become a source of unnecessary traffic if the applications on the network are inherently chatty, a trade-off inherited by any Layer 2 network, physical or virtual.</p>
<p>The BSL 1.1 license is also different from a classic permissive license; check it if you have strict compliance constraints around the type of open-source license accepted in your organization.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://www.wireguard.com/" rel="nofollow">WireGuard</a>
</strong>: simpler, faster, but strictly Layer 3; the right choice when you don&rsquo;t need broadcast/multicast.</li>
<li><strong><a href="https://tailscale.com/" rel="nofollow">Tailscale</a>
</strong>: same Layer 3 philosophy as WireGuard, but with automatic coordination and SSO; doesn&rsquo;t solve the Layer 2 need though.</li>
<li><strong>Physical VLANs/VXLAN over a dedicated private network</strong>: if you already have your own infrastructure between locations, a classic VXLAN tunnel can be more predictable than a public overlay SDN.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If your real need is &ldquo;these devices need to behave as if they&rsquo;re on the same cable,&rdquo; ZeroTier is one of the few tools that solves this properly, with native Layer 2 support and a solid rules engine for internal segmentation. If your need is just &ldquo;these servers need to talk to each other securely,&rdquo; check WireGuard first, it&rsquo;s simpler to operate and understand.</p>
<p>Want to test a self-hosted ZeroTier controller or a stable network node? Start from a <a href="/en/virtual-servers/vps/">VPS</a>
 with us, or let us configure it through <a href="/en/services/server-administration/">server administration</a>
.</p>
]]></content:encoded><category>Network</category><category>VPN</category></item><item><title>NetBird: a peer-to-peer WireGuard mesh VPN with a fully self-hostable control plane</title><link>https://dreamserver.ro/en/blog/netbird-wireguard-mesh-vpn/</link><pubDate>Wed, 29 Apr 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/netbird-wireguard-mesh-vpn/</guid><description>NetBird is a peer-to-peer mesh VPN built on WireGuard, with both the management and signal server fully self-hostable. What it offers and how to run it on a VPS.</description><content:encoded><![CDATA[<p>You have a laptop, a couple of production servers, a NAS at home, and a contractor who needs temporary access to one database. The classic answer: point-to-point WireGuard, keys generated by hand, <code>wg-quick</code> on every machine, and an <code>AllowedIPs</code> line you edit every time a new peer shows up. It works, until the mesh grows to a dozen nodes and every new peer means touching the config on the other eleven.</p>
<p>This is exactly the gap managed mesh VPNs fill: Tailscale, ZeroTier and others solve it, but with a trade-off - the control plane (identity, topology, access policy) lives on someone else&rsquo;s servers. <a href="https://github.com/netbirdio/netbird" rel="nofollow">NetBird</a>
 (<a href="https://netbird.io" rel="nofollow">netbird.io</a>
) is the open-source project (BSD-3 licensed) that keeps the same peer-to-peer WireGuard model but makes the entire control plane self-hostable: both the management server and the signal server that helps pairs of nodes negotiate a direct connection. We evaluated it as an alternative to the commercial stack for cases where you want a mesh VPN without your network topology living on a third party&rsquo;s infrastructure.</p>
<h2 id="what-netbird-actually-is">What NetBird actually is</h2>
<p>NetBird builds an overlay network on top of WireGuard: every node (server, laptop, container, phone) gets a cryptographic identity and an IP address from a private range, and traffic between nodes travels, ideally, directly between them, encrypted with WireGuard, without passing through a central hub. The hard part - establishing the connection - uses an ICE-like mechanism (Interactive Connectivity Establishment), similar to what WebRTC uses: nodes first attempt a direct peer-to-peer connection (with NAT hole punching where possible), and if NAT traversal fails (double symmetric NAT, restrictive firewalls), NetBird falls back to a relay server so you don&rsquo;t end up with no connectivity at all.</p>
<p>The server side is two distinct components, both of which you run on your own infrastructure in the self-hosted setup:</p>
<ul>
<li><strong>Management server</strong>: tracks nodes, groups, access policies and registration keys. It&rsquo;s the &ldquo;brain&rdquo; of the network.</li>
<li><strong>Signal server</strong>: a signaling channel through which two nodes exchange what they need (WireGuard public keys, connection candidates) before establishing the actual tunnel. It never sees application traffic, only coordinates the handshake.</li>
</ul>
<p>User identity isn&rsquo;t reinvented: NetBird plugs into any OIDC/SSO provider (Keycloak, Authentik, Zitadel, Google, and others), so adding or removing a teammate from the network is essentially a matter of managing an account in the IdP you already run.</p>
<h3 id="why-this-matters">Why this matters</h3>
<p>The difference from a commercial mesh VPN isn&rsquo;t cosmetic. If the vendor behind the service shuts down, loses your data, or abruptly changes its pricing, a self-hosted control plane means that&rsquo;s a technical problem you can solve yourself, not a negotiation with a vendor. It comes down to whether the full map of your infrastructure (who talks to whom, what access rules exist) lives on your servers or someone else&rsquo;s. For teams with compliance requirements, or anyone who simply doesn&rsquo;t want a critical external dependency on connectivity, that difference is real.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li>A peer-to-peer overlay network built on WireGuard, with automatic private addressing per node</li>
<li>NAT traversal via ICE/STUN with fallback to a TURN-like relay when direct traversal fails</li>
<li>Authentication via SSO/OIDC (Keycloak, Authentik, Zitadel, Google, and others), so you don&rsquo;t have to build a separate user system</li>
<li>Group-based access policies (who can talk to whom, on which ports), not just &ldquo;everyone sees everything&rdquo;</li>
<li>Setup keys for automatically registering nodes, useful for scripted provisioning or ephemeral containers</li>
<li>Cross-platform clients (Linux, Windows, macOS, mobile) plus a CLI for automation</li>
<li>Written in Go, easy to build and run in a container</li>
</ul>
<h2 id="installing-netbird-on-a-vps">Installing NetBird on a VPS</h2>
<p>The simplest way to get the self-hosted stack running is the official install script on a dedicated <a href="/en/virtual-servers/vps/">VPS</a>
, separate from the nodes you want to join to the mesh. You&rsquo;ll need your own domain (the management API and signal server are exposed over TLS) and, ideally, an OIDC IdP already configured.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># on a clean Debian/Ubuntu VPS, with a domain already pointing here</span>
</span></span><span style="display:flex;"><span>export NETBIRD_DOMAIN<span style="color:#f92672">=</span>netbird.example.com
</span></span><span style="display:flex;"><span>curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started-with-zitadel.sh | bash
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># once the stack (management + signal + Zitadel + Caddy) is up,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># add a client node on any machine with:</span>
</span></span><span style="display:flex;"><span>curl -fsSL https://pkgs.netbird.io/install.sh | sh
</span></span><span style="display:flex;"><span>netbird up --management-url https://netbird.example.com
</span></span></code></pre></div><p>The official script brings up the whole stack via Docker Compose (management, signal, a Zitadel IdP if you don&rsquo;t already have one, and a Caddy reverse proxy for automatic TLS). If you already run an IdP, you can skip the Zitadel component and point <code>management.json</code> at your existing OIDC issuer manually.</p>
<p>On the VPS firewall, you&rsquo;ll need at minimum:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># HTTPS for the management API and UI</span>
</span></span><span style="display:flex;"><span>ufw allow 443/tcp
</span></span><span style="display:flex;"><span><span style="color:#75715e"># signal server, if exposed on its own port (by default it rides on 443/gRPC-web)</span>
</span></span><span style="display:flex;"><span>ufw allow 10000/tcp
</span></span><span style="display:flex;"><span><span style="color:#75715e"># TURN-like relay for NAT traversal fallback</span>
</span></span><span style="display:flex;"><span>ufw allow 3478/udp
</span></span><span style="display:flex;"><span>ufw allow 49152:65535/udp
</span></span></code></pre></div><p>The exact relay port depends on the configuration you chose (external coturn vs. the bundled relay); check <code>docker compose ps</code> after installation to confirm which ports each container actually listens on.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="access-policies-not-just-connectivity">Access policies, not just connectivity</h3>
<p>Out of the box, every node in the network can talk to every other node. The first thing worth configuring is groups (e.g. <code>servers</code>, <code>laptops</code>, <code>contractors</code>) and explicit policies restricting traffic between them. A temporary contractor doesn&rsquo;t need to see the production database just because they&rsquo;re on the same mesh.</p>
<h3 id="setup-keys-for-automated-provisioning">Setup keys for automated provisioning</h3>
<p>If you&rsquo;re adding nodes via Ansible or cloud-init - exactly the scenario of a freshly provisioned VPS - use an expiring, usage-limited setup key instead of authenticating each machine manually through a browser. The key is generated from the UI or API and passed as an environment variable to <code>netbird up --setup-key ...</code>.</p>
<h3 id="relay-as-a-safety-net-not-the-default-path">Relay as a safety net, not the default path</h3>
<p>If you notice most connections falling back to relay instead of direct peer-to-peer, check the NAT and firewall on both ends. A permanent relay means extra latency and a dependency on your relay infrastructure for every connection, precisely what you wanted to avoid by choosing a peer-to-peer mesh in the first place.</p>
<h3 id="the-signal-server-is-not-a-trust-boundary-for-traffic">The signal server is not a trust boundary for traffic</h3>
<p>It&rsquo;s easy to wrongly assume the signal server sees application traffic. It doesn&rsquo;t: it only brokers the WireGuard handshake. That simplifies the threat model, but it&rsquo;s worth spelling out clearly when explaining the architecture to a team or auditors.</p>
<h2 id="where-netbird-is-not-the-right-answer">Where NetBird is not the right answer</h2>
<p>If you only need 2-3 servers connected point-to-point and don&rsquo;t want to maintain another stack (management + signal + IdP), plain <code>wg-quick</code> is simpler and has fewer moving parts that can break. NetBird earns its keep once the node count grows or you need granular access policies and fast onboarding/offboarding through SSO, not for a static handful of nodes.</p>
<p>Self-hosting also means you&rsquo;re responsible for the availability of the management and signal servers: if both go down, already-connected nodes keep their existing WireGuard tunnels, but you can&rsquo;t add new nodes or change policies until the stack is back up. It&rsquo;s worth having a backup plan for the management database.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://tailscale.com" rel="nofollow">Tailscale</a>
</strong>: the same peer-to-peer WireGuard model, but the control plane is SaaS (there&rsquo;s also a self-hosted coordination server, Headscale, maintained by the community rather than Tailscale Inc.)</li>
<li><strong><a href="https://github.com/juanfont/headscale" rel="nofollow">Headscale</a>
</strong>: an open-source, unofficial reimplementation of the Tailscale control plane; good if you want the Tailscale client with a self-hosted server</li>
<li><strong><a href="https://www.zerotier.com" rel="nofollow">ZeroTier</a>
</strong>: a mesh VPN with its own protocol (not WireGuard), a layer-2 overlay, with a self-hosted controller option</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you&rsquo;ve already outgrown a handful of manually managed WireGuard peers and want to keep full control over topology and identity, NetBird is a solid choice: it&rsquo;s open-source, built on WireGuard (so the cryptography is well-vetted and stable), and self-hosting the entire control plane means you don&rsquo;t depend on the uptime or pricing decisions of an external vendor.</p>
<p>The stack (management, signal, optionally coturn, and an IdP) runs comfortably on a dedicated <a href="/en/virtual-servers/vps/">VPS</a>
 with modest resources for small-to-medium teams. If you want it in production but don&rsquo;t want to own all the maintenance yourself (patching, backups for the management database, monitoring the signal server), take a look at our <a href="/en/services/server-administration/">server administration</a>
 service, or our <a href="/en/network/">network section</a>
 for context on the kind of infrastructure such a stack would run on.</p>
]]></content:encoded><category>Network</category><category>VPN</category></item><item><title>Tailscale: a mesh network on top of WireGuard, without hand-wiring tunnels</title><link>https://dreamserver.ro/en/blog/tailscale-zero-config-vpn/</link><pubDate>Sat, 25 Apr 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/tailscale-zero-config-vpn/</guid><description>Tailscale takes the WireGuard engine and adds automatic key coordination, NAT traversal, and SSO. How it works, what's free, and where it stays a proprietary SaaS.</description><content:encoded><![CDATA[<p>You&rsquo;ve got a working WireGuard tunnel between two servers, but now you need to add a third node, then a colleague&rsquo;s laptop from another country, then someone&rsquo;s phone that&rsquo;s permanently behind an annoying NAT that won&rsquo;t accept inbound connections. Suddenly the public-key matrix you were tracking by hand becomes a 20-row spreadsheet, and one wrong <code>AllowedIPs</code> entry breaks everything. That&rsquo;s exactly the problem Tailscale was built to solve.</p>
<p><a href="https://tailscale.com/" rel="nofollow">Tailscale</a>
 is a mesh VPN network built on top of the WireGuard protocol. The clients are open source (BSD-3 license), but the control plane that coordinates nodes is a proprietary SaaS service. At DreamServer we run plain WireGuard with wg-easy for our own needs; we evaluated Tailscale as an alternative for teams that want zero-config and don&rsquo;t want to manage keys by hand.</p>
<h2 id="what-tailscale-actually-is">What Tailscale actually is</h2>
<p>Under the hood, every data packet still travels over WireGuard: the same cryptography, Curve25519 and ChaCha20-Poly1305, the same point-to-point tunnel principles. The difference is what happens around that core. Tailscale runs a coordination server (control plane) that knows the identity of every device, automatically exchanges and refreshes public keys between nodes, and decides which nodes are allowed to talk to each other based on the policies you define.</p>
<p>Actual data traffic tries to stay peer-to-peer, direct between the two endpoints, even if both are behind different NATs. When a direct connection can&rsquo;t be established (strict symmetric NATs, aggressive corporate firewalls), Tailscale falls back to its own relays called DERP (Designated Encrypted Relay for Packets), which route end-to-end encrypted traffic without being able to read it.</p>
<h3 id="why-this-approach-matters">Why this approach matters</h3>
<p>The real problem Tailscale solves isn&rsquo;t encryption, it&rsquo;s coordination. Plain WireGuard requires every peer to know the other&rsquo;s public key and IP/port in advance. In a static network of 2-3 servers, that&rsquo;s trivial. In a dynamic network with users connecting from different networks every day, it becomes an operational nightmare. Tailscale moves that problem to a central server that handles auto-discovery and key distribution, and users authenticate via SSO (Google, GitHub, Okta, Microsoft) instead of receiving a config file over Slack.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>MagicDNS</strong>: every device on the network gets a stable DNS name (<code>server1.tail-scale.ts.net</code>), no more editing <code>/etc/hosts</code>.</li>
<li><strong>Declarative ACLs</strong>: a JSON/HuJSON file defines who can talk to whom, on which ports, far more granular than <code>AllowedIPs</code>.</li>
<li><strong>Native SSO</strong>: login through the identity providers you already use, with instant revocation when an employee leaves the organization.</li>
<li><strong>Automatic NAT traversal</strong>: no more manual port forwarding on a router for every new node.</li>
<li><strong>Exit nodes and subnet routers</strong>: a single node can expose an entire subnet or act as an exit gateway for all traffic, without reconfiguring the rest of the fleet.</li>
<li><strong>Clients on every platform</strong>, including mobile, with a simple GUI.</li>
</ul>
<h2 id="installing-tailscale-on-a-vps">Installing Tailscale on a VPS</h2>
<p>Installing the client is minimal regardless of distro:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -fsSL https://tailscale.com/install.sh | sh
</span></span><span style="display:flex;"><span>tailscale up --ssh --accept-routes
</span></span></code></pre></div><p>The <code>tailscale up</code> command opens an authentication link in a browser (or prints one in the terminal for headless servers), where you log in with your identity and authorize the node. For a server that needs to expose an internal subnet to other nodes on the network:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>tailscale up --advertise-routes<span style="color:#f92672">=</span>10.0.0.0/24 --advertise-exit-node
</span></span></code></pre></div><p>The <code>tailscale0</code> interface automatically gets an address from the <code>100.x.x.x</code> space (CGNAT range, reserved by Tailscale). You don&rsquo;t need a publicly exposed admin port, but if you run an additional local daemon or an exit node with extra services, lock them down explicitly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw default deny incoming
</span></span><span style="display:flex;"><span>ufw allow in on tailscale0
</span></span></code></pre></div><p>For a stable coordination node (subnet router, exit node) you need a VPS with predictable uptime; check our <a href="/en/virtual-servers/vps/">VPS plans</a>
 if you want to run it on our infrastructure.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="acls-actually-need-to-be-written-not-just-enabled">ACLs actually need to be written, not just enabled</h3>
<p>Without explicit policies in the ACL file, Tailscale defaults to permissive (every node on the tailnet can talk to every other node). For any production environment, write explicit policies like &ldquo;group X can only reach tag Y on port Z&rdquo; before adding real users to the network.</p>
<h3 id="tag-devices-dont-rely-on-user-identity">Tag devices, don&rsquo;t rely on user identity</h3>
<p>Authorize servers based on tags (<code>tag:prod-db</code>, <code>tag:ci-runner</code>), not on the account of the user who originally enrolled them. If that user leaves the organization and you revoke their access, tagged servers stay functional.</p>
<h3 id="keep-exit-node-separate-from-subnet-router">Keep exit node separate from subnet router</h3>
<p>Don&rsquo;t combine, without reason, the exit-node role (all of a client&rsquo;s internet traffic routes through that node) with the subnet-router role (exposes an internal subnet). They&rsquo;re different use cases, and enabling them on separate nodes avoids a single point through which everything flows.</p>
<h3 id="check-derp-if-latency-seems-high">Check DERP if latency seems high</h3>
<p>If you notice unexpectedly high latency between two nodes, check <code>tailscale netcheck</code> to see whether the connection fell back to a DERP relay instead of going direct. That&rsquo;s a sign NAT traversal failed, usually because of an aggressive corporate firewall on one side.</p>
<h2 id="where-tailscale-is-not-the-right-answer">Where Tailscale is not the right answer</h2>
<p>The most important trade-off, stated plainly: Tailscale&rsquo;s control plane is proprietary. If your internal policy requires all network infrastructure to be fully self-hosted, or you operate in an environment where you can&rsquo;t accept an external dependency for access coordination, plain Tailscale doesn&rsquo;t qualify. <a href="https://github.com/juanfont/headscale" rel="nofollow">Headscale</a>
 exists, an open-source reimplementation of the control plane compatible with official Tailscale clients, but it requires you to operate and maintain that server yourself.</p>
<p>For very small, static networks (2 servers that never change), it&rsquo;s also not worth the added complexity: plain WireGuard stays easier to understand end to end.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong>Plain <a href="https://www.wireguard.com/" rel="nofollow">WireGuard</a>
</strong> (with <a href="https://github.com/wg-easy/wg-easy" rel="nofollow">wg-easy</a>
 for a UI): no external dependency, ideal for small or static networks where you want full control.</li>
<li><strong><a href="https://github.com/juanfont/headscale" rel="nofollow">Headscale</a>
</strong>: open-source control plane, compatible with Tailscale clients, for anyone who wants the functionality without the SaaS.</li>
<li><strong><a href="https://www.zerotier.com/" rel="nofollow">ZeroTier</a>
</strong>: alternative with native Layer 2 support, its own protocol, its own control-plane model (partially self-hostable via &ldquo;moons&rdquo; and self-hosted controllers).</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you have a distributed team, devices connecting from different networks every day, and you want to avoid manually maintaining WireGuard keys and configs, Tailscale is worth trying: the free tier for personal use is generous, and setup is, literally, a <code>tailscale up</code> and a login. Just be aware of the trade-off: the control plane is SaaS, not yours, and if that&rsquo;s a problem, Headscale or plain WireGuard remain the options that give you full control.</p>
<p>Need a stable node for an exit node or subnet router? Start from a <a href="/en/virtual-servers/vps/">VPS</a>
 with us, or let our team configure it through <a href="/en/services/server-administration/">server administration</a>
.</p>
]]></content:encoded><category>Network</category><category>VPN</category></item><item><title>WireGuard: the modern VPN that fits in a config file</title><link>https://dreamserver.ro/en/blog/wireguard-modern-vpn/</link><pubDate>Wed, 22 Apr 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/wireguard-modern-vpn/</guid><description>WireGuard brings modern cryptography and a few thousand lines of code straight into the Linux kernel. Why it's different from OpenVPN/IPsec, and how we run it.</description><content:encoded><![CDATA[<p>It&rsquo;s 2 AM, an OpenVPN tunnel just died for reasons nobody can reproduce, and you&rsquo;re staring at a 200-line config file trying to spot the one cipher mismatch between client and server. Or the more common version: you need to grant a colleague temporary access to an internal environment, and the process takes longer than the problem they wanted to solve. If you&rsquo;ve ever felt a VPN should be a tool, not a project you maintain, WireGuard is why things changed.</p>
<p><a href="https://www.wireguard.com/" rel="nofollow">WireGuard</a>
 is an open-source VPN protocol and implementation, GPLv2-licensed for the Linux kernel side, created by Jason A. Donenfeld. Its code lives directly in the Linux kernel (mainline since version 5.6), which makes it both extremely fast and extremely small: a few thousand lines of code, compared to the tens of thousands in the OpenVPN or IPsec stacks.</p>
<h2 id="what-wireguard-actually-is">What WireGuard actually is</h2>
<p>WireGuard isn&rsquo;t a &ldquo;VPN framework&rdquo; with dozens of operating modes, optional ciphers, and negotiation combinations. It&rsquo;s a protocol with a single, fixed cryptographic suite: Curve25519 for key exchange, ChaCha20-Poly1305 for authenticated encryption, BLAKE2s for hashing. There&rsquo;s no &ldquo;downgrade&rdquo; option to a weaker cipher, because there are no options at all. All traffic rides over UDP, no TCP-in-TCP handshake and none of the latency problems that brings.</p>
<h3 id="why-this-approach-matters">Why this approach matters</h3>
<p>OpenVPN and IPsec were designed 20+ years ago, when flexible algorithm negotiation seemed like a good idea: systems evolved, ciphers changed, you needed to be able to choose. In practice, that flexibility became attack surface. Every configuration option is a decision an administrator can get wrong, and every legacy compatibility mode is code that has to be audited and maintained. WireGuard chose the opposite: one correct path, no switches. The result is a codebase small enough for a single person to read and understand in a weekend, which is precisely what made a formal protocol audit possible before it entered the kernel.</p>
<p>The routing model is just as simple: each peer is identified by a public key, and the routing table is, in effect, a mapping between public keys and allowed subnets (&ldquo;cryptokey routing&rdquo;). No certificates, no PKI, no certificate revocation at 3 AM because something expired. You either have the peer&rsquo;s public key, or you don&rsquo;t.</p>
<h2 id="what-you-get-out-of-the-box">What you get out of the box</h2>
<ul>
<li><strong>Transparent roaming</strong>: a client that changes IP (moving from WiFi to mobile data) doesn&rsquo;t lose the tunnel; WireGuard rediscovers the endpoint on the next valid packet.</li>
<li><strong>Silence when idle</strong>: if no traffic is flowing, WireGuard doesn&rsquo;t send constant heartbeats, which makes it quieter on the network and friendlier to battery life on mobile.</li>
<li><strong>Near line-rate performance</strong>: in-kernel encryption removes the user-space/kernel-space context switches OpenVPN pays for.</li>
<li><strong>Declarative configuration</strong>: a peer is a few lines (<code>PublicKey</code>, <code>AllowedIPs</code>, <code>Endpoint</code>); no CA, no separate authentication server needed.</li>
<li><strong>Native cross-platform support</strong>: Linux kernel module, WireGuard-go userspace for BSD/macOS/Windows, official mobile clients.</li>
</ul>
<h2 id="installing-wireguard-on-a-vps">Installing WireGuard on a VPS</h2>
<p>The fastest way to get a working WireGuard server with a web UI and QR config generation for mobile clients is <a href="https://github.com/wg-easy/wg-easy" rel="nofollow">wg-easy</a>
, an open-source project that puts a dashboard on top of <code>wg-quick</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/wg-easy <span style="color:#f92672">&amp;&amp;</span> cd ~/wg-easy
</span></span><span style="display:flex;"><span>docker run -d <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --name wg-easy <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e WG_HOST<span style="color:#f92672">=</span>&lt;public-server-ip&gt; <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -e PASSWORD_HASH<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;$2a$12$...&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -v ~/wg-easy/etc-wireguard:/etc/wireguard <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 51821:51821/tcp <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -p 51820:51820/udp <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --cap-add NET_ADMIN <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --cap-add SYS_MODULE <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --sysctl net.ipv4.ip_forward<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --sysctl net.ipv4.conf.all.src_valid_mark<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --restart unless-stopped <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  ghcr.io/wg-easy/wg-easy
</span></span></code></pre></div><p>The admin port (51821/tcp) should never be open to the whole internet. Lock it down to the IPs you actually administer from:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 51820/udp
</span></span><span style="display:flex;"><span>ufw deny 51821/tcp
</span></span><span style="display:flex;"><span>ufw allow from &lt;your-ip&gt; to any port <span style="color:#ae81ff">51821</span> proto tcp
</span></span></code></pre></div><p>If you prefer a manual setup (no UI), a clean VPS with <code>wireguard-tools</code> installed and a few lines in <code>/etc/wireguard/wg0.conf</code> gets you running in under 5 minutes. Either way you need a VPS with a modern kernel and root access; check our <a href="/en/virtual-servers/vps/">VPS plans</a>
 if you want to start from scratch on our Bucharest infrastructure.</p>
<h2 id="configuration-patterns-worth-knowing">Configuration patterns worth knowing</h2>
<h3 id="allowedips-does-double-duty">AllowedIPs does double duty</h3>
<p>The <code>AllowedIPs</code> field has two roles: besides telling the kernel what traffic to route through the tunnel, it also defines what packets are accepted from that peer (source filtering). If you want split-tunnel (only traffic to your internal network goes through the VPN), put only that subnet there, not <code>0.0.0.0/0</code>.</p>
<h3 id="persistentkeepalive-for-clients-behind-nat">PersistentKeepalive for clients behind NAT</h3>
<p>If a client sits behind symmetric NAT (common on mobile networks or CGNAT), set <code>PersistentKeepalive = 25</code> on the client side to keep the NAT mapping open. Without it, the server may try to send traffic to an endpoint that no longer responds.</p>
<h3 id="one-key-per-device-never-shared">One key per device, never shared</h3>
<p>Generate a separate keypair for every device/peer. It&rsquo;s tempting to reuse one config between a laptop and a phone, but you lose the ability to revoke a single device&rsquo;s access without affecting the rest.</p>
<h3 id="mtu-matters-more-than-it-looks">MTU matters more than it looks</h3>
<p>Over PPPoE or other tunneled links, the default MTU can cause silent fragmentation. Lower the WireGuard interface MTU to 1420 (or even 1280 for maximum compatibility) if you see connections stalling only on large transfers.</p>
<h2 id="where-wireguard-is-not-the-right-answer">Where WireGuard is not the right answer</h2>
<p>WireGuard is a point-to-point tunnel protocol, not a fleet management platform. It has no native ACLs, no SSO, no central dashboard showing who&rsquo;s connected where, beyond whatever you build on top (like wg-easy). If you need per-user access policies, Google/Okta integration, or centralized management across dozens of offices and hundreds of users, you&rsquo;ll either build that layer yourself or pick a tool that already ships it.</p>
<p>Also, if you need Layer 2 networking (broadcast, multicast, device discovery like on a physical switch), WireGuard isn&rsquo;t a fit: it&rsquo;s strictly Layer 3, IP routing.</p>
<h2 id="alternatives-we-considered">Alternatives we considered</h2>
<ul>
<li><strong><a href="https://tailscale.com/" rel="nofollow">Tailscale</a>
</strong>: built on top of WireGuard, adds automatic key coordination, NAT traversal with DERP relays, SSO, and ACLs. Very easy to get started with, but the control plane remains a proprietary SaaS (Headscale exists as an open-source control-plane alternative).</li>
<li><strong><a href="https://www.zerotier.com/" rel="nofollow">ZeroTier</a>
</strong>: software-defined network that emulates an Ethernet switch over the internet, with Layer 2 support, its own protocol (not WireGuard).</li>
<li><strong>Classic OpenVPN/IPsec</strong>: still relevant where you already have infrastructure built on them, or need compatibility with legacy hardware that doesn&rsquo;t support WireGuard.</li>
</ul>
<h2 id="so-should-you-run-it">So, should you run it?</h2>
<p>If you need a fast, auditable, easy-to-understand point-to-point tunnel between offices, servers, or for administrative access to your infrastructure, WireGuard is the right choice in 2026. We actually run it ourselves: wg-easy sits on our docker hosts, plus a dedicated WireGuard VPN for operational access into our internal infrastructure, precisely because we don&rsquo;t want to depend on a CA that can expire at the worst possible moment.</p>
<p>If you want to run it on your own infrastructure, start from a <a href="/en/virtual-servers/vps/">VPS</a>
 with a modern kernel, or let us configure and maintain it through <a href="/en/services/server-administration/">server administration</a>
.</p>
]]></content:encoded><category>Network</category><category>VPN</category></item><item><title>Introducing the DreamServer Blog</title><link>https://dreamserver.ro/en/blog/introducing-the-dreamserver-blog/</link><pubDate>Sat, 18 Apr 2026 08:00:00 +0300</pubDate><author>Cristian Trusca</author><guid isPermaLink="true">https://dreamserver.ro/en/blog/introducing-the-dreamserver-blog/</guid><description>We are starting a blog to publish promotions, campaigns, tutorials, recommendations and technical articles about the hosting services we run - dedicated servers, VDS, VPS and everything around them.</description><content:encoded><![CDATA[<p>Welcome to the DreamServer blog.</p>
<p>This is where we will publish <strong>promotions, campaigns, tutorials, recommendations and technical articles</strong> about the hosting services we run. If it helps a customer configure, deploy or troubleshoot a dedicated server, VDS or VPS, we want to write it down here.</p>
<h2 id="what-to-expect">What to expect</h2>
<ul>
<li><strong>Promotions and campaigns</strong> - when we launch a deal, drop a price, bundle a service or open a new configuration, you will read it here first.</li>
<li><strong>Tutorials</strong> - step-by-step guides with commands you can paste and configuration files you can reuse, focused on getting your servers up and running the way you actually want them.</li>
<li><strong>Recommendations</strong> - platforms, control panels, operating systems and applications that work well on our infrastructure, and how to pick between the usual alternatives.</li>
<li><strong>Technical articles</strong> - dedicated server setup, VDS and VPS management, Linux administration, networking, storage, backups and security hardening - the same topics that come up in our support tickets, written up once so you can find the answer faster next time.</li>
</ul>
<h2 id="how-we-write">How we write</h2>
<p>Honest, source-linked, no fluff. When something needs proof we cite the documentation, the RFC or the vendor knowledge base. When we make a mistake we correct it inline with a dated note.</p>
<h2 id="stay-in-touch">Stay in touch</h2>
<ul>
<li><strong>RSS</strong> - subscribe in your feed reader at <a href="/en/blog/index.xml">/en/blog/index.xml</a>
.</li>
<li><strong>Email</strong> - <a href="mailto:contact@dreamserver.ro">contact@dreamserver.ro</a>
 for sales, <a href="mailto:noc@dreamserver.ro">noc@dreamserver.ro</a>
 for network operations, or open a ticket from the <a href="/client/">client area</a>
.</li>
<li><strong>Services</strong> - browse <a href="/en/dedicated-servers/bladeservers/">dedicated servers</a>
, <a href="/en/virtual-servers/vps/">VPS</a>
 and <a href="/en/virtual-servers/vds/">VDS</a>
.</li>
</ul>
<p>Thanks for stopping by. More posts coming soon.</p>
]]></content:encoded><category>Announcements</category></item></channel></rss>