Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: https://portalzine.de/feed?clean

  1. <?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
  2. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  3. xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  4. xmlns:dc="http://purl.org/dc/elements/1.1/"
  5. xmlns:atom="http://www.w3.org/2005/Atom"
  6. xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  7. xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  8. >
  9.  
  10. <channel>
  11. <title>portalZINE NMN | Development meets Creativity</title>
  12. <atom:link href="https://portalzine.de/feed/?clean" rel="self" type="application/rss+xml" />
  13. <link>https://portalzine.de</link>
  14. <description>Development meets Creativity</description>
  15. <lastBuildDate>Thu, 24 Apr 2025 09:19:24 +0000</lastBuildDate>
  16. <language>en-US</language>
  17. <sy:updatePeriod>
  18. hourly </sy:updatePeriod>
  19. <sy:updateFrequency>
  20. 1 </sy:updateFrequency>
  21.  
  22. <image>
  23. <url>https://portalzine.de/wp-content/uploads/2025/02/cropped-favicon-32x32.png</url>
  24. <title>portalZINE NMN | Development meets Creativity</title>
  25. <link>https://portalzine.de</link>
  26. <width>32</width>
  27. <height>32</height>
  28. </image>
  29. <item>
  30. <title>Making Your Life Easier: Debugging with Monolog and Wonolog in WordPress</title>
  31. <link>https://portalzine.de/making-your-life-easier-debugging-with-monolog-and-wonolog-in-wordpress/</link>
  32. <comments>https://portalzine.de/making-your-life-easier-debugging-with-monolog-and-wonolog-in-wordpress/#respond</comments>
  33. <dc:creator><![CDATA[]]></dc:creator>
  34. <pubDate>Thu, 24 Apr 2025 09:19:21 +0000</pubDate>
  35. <category><![CDATA[Development]]></category>
  36. <category><![CDATA[Debugging]]></category>
  37. <category><![CDATA[Deployment]]></category>
  38. <category><![CDATA[PHP]]></category>
  39. <category><![CDATA[WordPress]]></category>
  40. <guid isPermaLink="false">https://portalzine.de/?p=8456</guid>
  41.  
  42. <description><![CDATA[Why Bother with Proper Logging? Let&#8217;s be honest – debugging can be a real pain. We&#8217;ve all been there, staring at a blank screen with no clue what went wrong. Or worse, getting vague error messages that lead nowhere. That&#8217;s where good logging comes in! PHP&#8217;s built-in error reporting is&#8230; well, it&#8217;s there. But it&#8217;s [&#8230;]]]></description>
  43. <content:encoded><![CDATA[
  44. <h2 class="wp-block-heading">Why Bother with Proper Logging?</h2>
  45.  
  46.  
  47.  
  48. <p>Let&#8217;s be honest – debugging can be a real pain. We&#8217;ve all been there, staring at a blank screen with no clue what went wrong. Or worse, getting vague error messages that lead nowhere. That&#8217;s where <a href="https://en.wikipedia.org/wiki/Logging_(software)" target="_blank" rel="noopener">good logging</a> comes in!</p>
  49.  
  50.  
  51.  
  52. <p><a href="https://www.php.net/manual/en/function.error-reporting.php" target="_blank" rel="noopener">PHP&#8217;s built-in error reporting</a> is&#8230; well, it&#8217;s there. But it&#8217;s pretty basic. It&#8217;s like trying to fix a complex machine with just a hammer. You need more specialized tools for serious development work.</p>
  53.  
  54.  
  55.  
  56. <p>That&#8217;s where <a href="https://github.com/Seldaek/monolog" target="_blank" rel="noopener">Monolog</a> enters the picture. Think of it as your debugging Swiss Army knife – it gives you a ton of options to see what&#8217;s really happening in your code.</p>
  57.  
  58.  
  59.  
  60. <p><strong>Looking for a log navigator? </strong>Checkout my article about <a href="https://portalzine.de/lnav-a-powerful-tool-for-log-file-debugging/">LNAV</a>. <br><strong>Looking for a external error logger or Sentry alternative?</strong> Checkout these &#8230; <a href="https://portalzine.de/day-29-bugsink-self-hosted-error-tracking-7-days-of-docker/">Bugsink</a> / <a href="https://portalzine.de/day-3-glitchtip-7-days-of-docker/">Glitchtip</a>.</p>
  61.  
  62.  
  63.  
  64. <h2 class="wp-block-heading">What&#8217;s Monolog Anyway?</h2>
  65.  
  66.  
  67.  
  68. <p><a href="https://seldaek.github.io/monolog/" target="_blank" rel="noopener">Monolog</a> is basically the rock star of PHP logging libraries. It&#8217;s what all the cool frameworks use (<a href="https://laravel.com/docs/10.x/logging" target="_blank" rel="noopener">Laravel</a>, <a href="https://symfony.com/doc/current/logging.html" target="_blank" rel="noopener">Symfony</a> – you name it). With millions of downloads, it&#8217;s become the go-to solution for anyone who needs to know what&#8217;s happening in their application.</p>
  69.  
  70.  
  71.  
  72. <p>The beauty of Monolog is how flexible it is. Want to log to files? Sure. Databases? No problem. Email, Slack, or pretty much anywhere else? Monolog has got you covered. It plays nicely with other systems too, thanks to following the <a href="https://www.php-fig.org/psr/psr-3/" target="_blank" rel="noopener">PSR-3 standard</a> (fancy talk for &#8220;it works well with other logging tools&#8221;).</p>
  73.  
  74.  
  75.  
  76. <h2 class="wp-block-heading">Why Monolog Rocks for Debugging</h2>
  77.  
  78.  
  79.  
  80. <p>Here&#8217;s why Monolog is your new best friend when it comes to squashing bugs:</p>
  81.  
  82.  
  83.  
  84. <ul class="wp-block-list">
  85. <li><strong>It&#8217;s multi-talented</strong> – Send your logs to different places at once (like keeping detailed logs in a file while getting urgent errors via email)</li>
  86.  
  87.  
  88.  
  89. <li><strong>It knows how serious things are</strong> – With eight different levels from &#8220;just FYI&#8221; to &#8220;everything&#8217;s on fire!&#8221;, you can filter what matters</li>
  90.  
  91.  
  92.  
  93. <li><strong>It&#8217;s context-aware</strong> – Automatically add extra info to your logs like user data or request details</li>
  94.  
  95.  
  96.  
  97. <li><strong>It&#8217;s pretty</strong> – Customize how your logs look so they&#8217;re actually readable</li>
  98.  
  99.  
  100.  
  101. <li><strong>It&#8217;s organized</strong> – Group logs by channels so you&#8217;re not searching through a haystack</li>
  102. </ul>
  103.  
  104.  
  105.  
  106. <p>All this means you spend less time scratching your head and more time actually fixing problems!</p>
  107.  
  108.  
  109.  
  110. <h2 class="wp-block-heading">Getting Started with Monolog</h2>
  111.  
  112.  
  113.  
  114. <h3 class="wp-block-heading">Installation: Quick and Painless</h3>
  115.  
  116.  
  117.  
  118. <p>First things first, let&#8217;s get Monolog installed. It&#8217;s as simple as:</p>
  119.  
  120.  
  121.  
  122. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">composer require monolog/monolog</pre></div>
  123.  
  124.  
  125.  
  126. <p>That&#8217;s it! One line and you&#8217;re ready to roll. (If you&#8217;re new to Composer, check out the <a href="https://getcomposer.org/doc/00-intro.md" target="_blank" rel="noopener">getting started guide</a> first.)</p>
  127.  
  128.  
  129.  
  130. <h3 class="wp-block-heading">Basic Setup: Your First Debug Logger</h3>
  131.  
  132.  
  133.  
  134. <p>Here&#8217;s a simple setup to get you started:</p>
  135.  
  136.  
  137.  
  138. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Get Composer's autoloader in the mix
  139. require __DIR__ . '/vendor/autoload.php';
  140.  
  141. // Bring in what we need from Monolog
  142. use Monolog\Logger;
  143. use Monolog\Handler\StreamHandler;
  144. use Monolog\Processor\IntrospectionProcessor;
  145.  
  146. // Create our debug superhero
  147. $logger = new Logger('debug');
  148.  
  149. // Tell it where to save the day (I mean, logs)
  150. $logger---&gt;pushHandler(new StreamHandler(__DIR__.'/logs/debug.log', Logger::DEBUG));
  151.  
  152. // Give it some extra powers (like knowing where the log came from)
  153. $logger-&gt;pushProcessor(new IntrospectionProcessor());
  154.  
  155. // Now let's use it!
  156. $logger-&gt;debug('Checking if user #123 is signed in', ['username' =&gt; 'bug_squasher']);</pre></div>
  157.  
  158.  
  159.  
  160. <p>This creates a logger that will capture all your debug info in a file and tell you exactly which line of code triggered each log. Pretty neat, right?</p>
  161.  
  162.  
  163.  
  164. <h2 class="wp-block-heading">Making the Most of Monolog</h2>
  165.  
  166.  
  167.  
  168. <h3 class="wp-block-heading">Log Levels: Choose Your Battle</h3>
  169.  
  170.  
  171.  
  172. <p>Monolog gives you <a href="https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md#log-levels" target="_blank" rel="noopener">eight different log levels</a>, from &#8220;I&#8217;m just being chatty&#8221; to &#8220;EVERYTHING IS BROKEN!&#8221;:</p>
  173.  
  174.  
  175.  
  176. <ul class="wp-block-list">
  177. <li><strong>DEBUG</strong>: &#8220;Hey, just FYI, this happened&#8221; (for development)</li>
  178.  
  179.  
  180.  
  181. <li><strong>INFO</strong>: &#8220;Something normal but noteworthy happened&#8221;</li>
  182.  
  183.  
  184.  
  185. <li><strong>NOTICE</strong>: &#8220;This is unusual but not worrying&#8221;</li>
  186.  
  187.  
  188.  
  189. <li><strong>WARNING</strong>: &#8220;This isn&#8217;t great but we&#8217;re still functioning&#8221;</li>
  190.  
  191.  
  192.  
  193. <li><strong>ERROR</strong>: &#8220;Houston, we have a problem&#8221;</li>
  194.  
  195.  
  196.  
  197. <li><strong>CRITICAL</strong>: &#8220;Major component down!&#8221;</li>
  198.  
  199.  
  200.  
  201. <li><strong>ALERT</strong>: &#8220;Someone needs to fix this NOW&#8221;</li>
  202.  
  203.  
  204.  
  205. <li><strong>EMERGENCY</strong>: &#8220;The building is on fire and everyone&#8217;s panicking&#8221;</li>
  206. </ul>
  207.  
  208.  
  209.  
  210. <p>When you&#8217;re debugging, you&#8217;ll mostly use the first three levels, saving the scarier ones for actual problems. Here&#8217;s how you might use them:</p>
  211.  
  212.  
  213.  
  214. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// When you're just being nosy about what's happening
  215. $logger-&gt;debug('Here's what that variable contains', ['the_var' =&gt; $mysterious_variable]);
  216.  
  217. // When tracking normal application flow
  218. $logger-&gt;info('User tried to log in', ['username' =&gt; $username]);
  219.  
  220. // When something's fishy but not broken
  221. $logger-&gt;warning('That image upload failed, using the default instead', ['error' =&gt; $error]);</pre></div>
  222.  
  223.  
  224.  
  225. <h3 class="wp-block-heading">Context: The Secret Sauce</h3>
  226.  
  227.  
  228.  
  229. <p>Want to make your logs actually useful? Add context! Compare these two:</p>
  230.  
  231.  
  232.  
  233. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Boring and unhelpful
  234. $logger-&gt;debug('Payment processed');
  235.  
  236. // Detailed and actually useful!
  237. $logger-&gt;debug('Payment processed', [
  238.    'amount' =&gt; $amount,
  239.    'currency' =&gt; 'USD',
  240.    'customer_id' =&gt; $customer_id,
  241.    'payment_method' =&gt; $method,
  242.    'successful' =&gt; $success
  243. ]);</pre></div>
  244.  
  245.  
  246.  
  247. <p>See the difference? The second one tells you what&#8217;s actually going on. When you come back to these logs later (and trust me, you will), you&#8217;ll thank yourself for the extra details.</p>
  248.  
  249.  
  250.  
  251. <h2 class="wp-block-heading">Cool Monolog Tricks for Debugging</h2>
  252.  
  253.  
  254.  
  255. <h3 class="wp-block-heading">Rotating Log Files (So Your Disk Doesn&#8217;t Explode)</h3>
  256.  
  257.  
  258.  
  259. <p>Nobody wants a 10GB log file. Here&#8217;s how to create <a href="https://github.com/Seldaek/monolog/blob/main/src/Monolog/Handler/RotatingFileHandler.php" target="_blank" rel="noopener">daily logs that clean up after themselves</a>:</p>
  260.  
  261.  
  262.  
  263. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">use Monolog\Handler\RotatingFileHandler;
  264.  
  265. // Logs rotate daily and we keep a week's worth
  266. $logger-&gt;pushHandler(new RotatingFileHandler(
  267.    __DIR__.'/logs/debug.log',
  268.    7,  // days to keep
  269.    Logger::DEBUG
  270. ));</pre></div>
  271.  
  272.  
  273.  
  274. <h3 class="wp-block-heading">Browser Console Magic</h3>
  275.  
  276.  
  277.  
  278. <p>When working on frontend stuff, why not send debug info right to your browser&#8217;s console?</p>
  279.  
  280.  
  281.  
  282. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">use Monolog\Handler\BrowserConsoleHandler;
  283.  
  284. // Only in development, obviously!
  285. if (APP_ENV === 'dev') {
  286.    $logger-&gt;pushHandler(new BrowserConsoleHandler(Logger::DEBUG));
  287. }</pre></div>
  288.  
  289.  
  290.  
  291. <h3 class="wp-block-heading">Wake-Me-Up Alerts</h3>
  292.  
  293.  
  294.  
  295. <p>For those &#8220;someone fix this now!&#8221; moments:</p>
  296.  
  297.  
  298.  
  299. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">use Monolog\Handler\NativeMailerHandler;
  300.  
  301. $logger-&gt;pushHandler(new NativeMailerHandler(
  302.    'you@example.com',
  303.    '<img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SITE ON FIRE <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" />',
  304.    'system@example.com',
  305.    Logger::CRITICAL
  306. ));</pre></div>
  307.  
  308.  
  309.  
  310. <h3 class="wp-block-heading">The Multi-Logger Approach</h3>
  311.  
  312.  
  313.  
  314. <p>Why settle for one logging method when you can have several? Here&#8217;s a setup that gives you the best of all worlds:</p>
  315.  
  316.  
  317.  
  318. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// All the nitty-gritty details go here
  319. $logger-&gt;pushHandler(new StreamHandler(__DIR__.'/logs/debug.log', Logger::DEBUG));
  320.  
  321. // Just the "hmm, that's interesting" stuff and above
  322. $logger-&gt;pushHandler(new StreamHandler(__DIR__.'/logs/notice.log', Logger::NOTICE));
  323.  
  324. // Email me if something's actually broken
  325. $logger-&gt;pushHandler(new NativeMailerHandler(
  326.    'dev@example.com',
  327.    'Site Error Alert',
  328.    'alerts@example.com',
  329.    Logger::ERROR
  330. ));</pre></div>
  331.  
  332.  
  333.  
  334. <h3 class="wp-block-heading">Automatic Context: Be Lazy (In a Good Way)</h3>
  335.  
  336.  
  337.  
  338. <p>Why manually add the same info to every log when you can <a href="https://github.com/Seldaek/monolog/blob/main/doc/02-handlers-formatters-processors.md#processors" target="_blank" rel="noopener">automate it</a>?</p>
  339.  
  340.  
  341.  
  342. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">use Monolog\Processor\WebProcessor;
  343. use Monolog\Processor\IntrospectionProcessor;
  344. use Monolog\Processor\MemoryUsageProcessor;
  345.  
  346. // Add info about the web request
  347. $logger-&gt;pushProcessor(new WebProcessor());
  348.  
  349. // Add file:line where the log happened
  350. $logger-&gt;pushProcessor(new IntrospectionProcessor());
  351.  
  352. // See if your code is memory-hungry
  353. $logger-&gt;pushProcessor(new MemoryUsageProcessor());</pre></div>
  354.  
  355.  
  356.  
  357. <p>Now every log entry automatically includes this info. It&#8217;s like having an assistant who fills in all the details you&#8217;d otherwise forget! Check out the <a href="https://github.com/Seldaek/monolog/tree/main/src/Monolog/Processor" target="_blank" rel="noopener">full list of available processors</a>.</p>
  358.  
  359.  
  360.  
  361. <h2 class="wp-block-heading">Wonolog: Monolog for WordPress Fans</h2>
  362.  
  363.  
  364.  
  365. <p>If you&#8217;re a WordPress developer, you might be thinking &#8220;This Monolog thing sounds great, but I work in WordPress-land!&#8221; Well, you&#8217;re in luck – that&#8217;s exactly where <a href="https://github.com/inpsyde/Wonolog" target="_blank" rel="noopener">Wonolog</a> comes in.</p>
  366.  
  367.  
  368.  
  369. <h3 class="wp-block-heading">What&#8217;s Wonolog All About?</h3>
  370.  
  371.  
  372.  
  373. <p>Wonolog is basically Monolog wearing a WordPress costume. It&#8217;s a special package that connects <a href="https://developer.wordpress.org/plugins/hooks/" target="_blank" rel="noopener">WordPress&#8217;s way of doing things</a> with Monolog&#8217;s powerful logging features. The team at <a href="https://inpsyde.com/" target="_blank" rel="noopener">Inpsyde</a> created it to let WordPress developers join the logging party.</p>
  374.  
  375.  
  376.  
  377. <p>The cool part? You don&#8217;t have to rewrite your WordPress code to use it. Wonolog listens for WordPress actions and hooks, then does the logging for you.</p>
  378.  
  379.  
  380.  
  381. <h2 class="wp-block-heading">Getting Wonolog Up and Running</h2>
  382.  
  383.  
  384.  
  385. <h3 class="wp-block-heading">Quick Install</h3>
  386.  
  387.  
  388.  
  389. <p>Just like Monolog, installation is a one-liner:</p>
  390.  
  391.  
  392.  
  393. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">composer require inpsyde/wonolog</pre></div>
  394.  
  395.  
  396.  
  397. <h3 class="wp-block-heading">Super Simple Setup</h3>
  398.  
  399.  
  400.  
  401. <p>Wonolog&#8217;s setup is ridiculously easy:</p>
  402.  
  403.  
  404.  
  405. <ol class="wp-block-list">
  406. <li>Make sure <a href="https://developer.wordpress.org/advanced-administration/wordpress/composer/" target="_blank" rel="noopener">Composer&#8217;s autoloader is loaded early in WordPress</a></li>
  407.  
  408.  
  409.  
  410. <li>Create a super-simple <a href="https://developer.wordpress.org/plugins/must-use/" target="_blank" rel="noopener">must-use plugin</a> with this tiny code:</li>
  411. </ol>
  412.  
  413.  
  414.  
  415. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// That's it. Seriously.
  416. Inpsyde\Wonolog\bootstrap();</pre></div>
  417.  
  418.  
  419.  
  420. <p>Tada! You now have powerful logging for WordPress. Wonolog will create daily log files in <code>{WP_CONTENT_DIR}/wonolog/{Y/m/d}.log</code> – for example, <code>/wp-content/wonolog/2023/04/24.log</code>.</p>
  421.  
  422.  
  423.  
  424. <h3 class="wp-block-heading">What Gets Logged Automatically</h3>
  425.  
  426.  
  427.  
  428. <p>Wonolog is smart about what it logs based on your <code>WP_DEBUG_LOG</code> setting:</p>
  429.  
  430.  
  431.  
  432. <ul class="wp-block-list">
  433. <li>If <code>WP_DEBUG_LOG</code> is <code>true</code>: &#8220;Log all the things!&#8221;</li>
  434.  
  435.  
  436.  
  437. <li>If <code>WP_DEBUG_LOG</code> is <code>false</code>: &#8220;Only log the scary stuff&#8221; (ERROR level and above)</li>
  438. </ul>
  439.  
  440.  
  441.  
  442. <p>Right out of the box, Wonolog catches:</p>
  443.  
  444.  
  445.  
  446. <ul class="wp-block-list">
  447. <li>PHP notices, warnings, and errors (oops!)</li>
  448.  
  449.  
  450.  
  451. <li>Exceptions that nobody caught (double oops!)</li>
  452.  
  453.  
  454.  
  455. <li>WordPress errors like database problems, HTTP API fails, wp_mail() issues, and those pesky 404s</li>
  456. </ul>
  457.  
  458.  
  459.  
  460. <h2 class="wp-block-heading">WordPress-Style Debugging with Wonolog</h2>
  461.  
  462.  
  463.  
  464. <h3 class="wp-block-heading">The WordPress Way to Log</h3>
  465.  
  466.  
  467.  
  468. <p>The real magic of Wonolog is how it works with <a href="https://developer.wordpress.org/plugins/hooks/actions/" target="_blank" rel="noopener">WordPress&#8217;s action system</a>. Want to log something? Just do:</p>
  469.  
  470.  
  471.  
  472. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Simple debug log
  473. do_action('wonolog.debug', 'Testing if shortcodes are working');
  474.  
  475. // With all the juicy details
  476. do_action(
  477.    'wonolog.debug',
  478.    'User updated their profile',
  479.    [
  480.        'user_id' =&gt; $user-&gt;ID,
  481.        'fields_changed' =&gt; $changed_fields,
  482.        'came_from' =&gt; wp_get_referer()
  483.    ]
  484. );</pre></div>
  485.  
  486.  
  487.  
  488. <p>See how natural that feels in WordPress? No need to create logger objects or remember Monolog syntax – just use <a href="https://developer.wordpress.org/reference/functions/do_action/" target="_blank" rel="noopener">WordPress actions</a> like you normally would.</p>
  489.  
  490.  
  491.  
  492. <h3 class="wp-block-heading">Keeping Things Organized with Channels</h3>
  493.  
  494.  
  495.  
  496. <p>If you&#8217;re working on a bigger project, you&#8217;ll want to organize your logs. Wonolog lets you create custom channels:</p>
  497.  
  498.  
  499.  
  500. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// In your e-commerce plugin
  501. do_action(
  502.    'wonolog.log',
  503.    [
  504.        'level' =&gt; 'debug',
  505.        'message' =&gt; 'New order being processed',
  506.        'context' =&gt; ['order_id' =&gt; $order_id, 'total' =&gt; $total],
  507.        'channel' =&gt; 'my_shop_orders'
  508.    ]
  509. );</pre></div>
  510.  
  511.  
  512.  
  513. <p>Now all your shop order logs are grouped together, making it way easier to track down issues.</p>
  514.  
  515.  
  516.  
  517. <h3 class="wp-block-heading">WordPress-Specific Debugging</h3>
  518.  
  519.  
  520.  
  521. <p>Here are some cool tricks for debugging WordPress components:</p>
  522.  
  523.  
  524.  
  525. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// See what database queries are running
  526. add_action('query', function($query) {
  527.    do_action(
  528.        'wonolog.debug',
  529.        'SQL query executed',
  530.        [
  531.            'query' =&gt; $query,
  532.            'backtrace' =&gt; wp_debug_backtrace_summary()
  533.        ],
  534.        'database'
  535.    );
  536. });
  537.  
  538. // Track REST API activity
  539. add_action('rest_api_init', function() {
  540.    add_filter('rest_pre_dispatch', function($result, $server, $request) {
  541.        do_action(
  542.            'wonolog.debug',
  543.            'REST API request received',
  544.            [
  545.                'endpoint' =&gt; $request-&gt;get_route(),
  546.                'method' =&gt; $request-&gt;get_method(),
  547.                'params' =&gt; $request-&gt;get_params()
  548.            ],
  549.            'rest_api'
  550.        );
  551.        return $result;
  552.    }, 10, 3);
  553. });</pre></div>
  554.  
  555.  
  556.  
  557. <h2 class="wp-block-heading">Taking Wonolog to the Next Level</h2>
  558.  
  559.  
  560.  
  561. <h3 class="wp-block-heading">Custom Log Files</h3>
  562.  
  563.  
  564.  
  565. <p>Want your logs somewhere specific? No problem:</p>
  566.  
  567.  
  568.  
  569. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">add_filter('wonolog.handler-factory', function($factory) {
  570.    // Create a custom handler for your plugin
  571.    $factory-&gt;addCustomFactory('my_plugin_handler', function(array $config) {
  572.        // Log to a specific file
  573.        return new StreamHandler(
  574.            WP_CONTENT_DIR . '/my-plugin/debug.log',
  575.            Logger::DEBUG
  576.        );
  577.    });
  578.    return $factory;
  579. });
  580.  
  581. // Use your custom handler
  582. add_filter('wonolog.default-hook-listeners', function($listeners) {
  583.    $listeners['debug']-&gt;useHandlers(['my_plugin_handler']);
  584.    return $listeners;
  585. });</pre></div>
  586.  
  587.  
  588.  
  589. <h3 class="wp-block-heading">Different Logging for Different Environments</h3>
  590.  
  591.  
  592.  
  593. <p>You don&#8217;t want the same logging setup in development and production. Here&#8217;s how to adjust based on environment:</p>
  594.  
  595.  
  596.  
  597. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">add_filter('wonolog.bootstrap-config', function($config) {
  598.    // Check which environment we're in
  599.    if (defined('WP_ENV') &amp;&amp; WP_ENV === 'development') {
  600.        // For development: log ALL the things!
  601.        $config['default_handlers']['minimum_level'] = Logger::DEBUG;
  602.        $config['use_buffer'] = false; // Log immediately
  603.    } elseif (defined('WP_ENV') &amp;&amp; WP_ENV === 'staging') {
  604.        // For staging: be a bit more selective
  605.        $config['default_handlers']['minimum_level'] = Logger::INFO;
  606.    } else {
  607.        // For production: just the important stuff
  608.        $config['default_handlers']['minimum_level'] = Logger::WARNING;
  609.    }
  610.    
  611.    return $config;
  612. });</pre></div>
  613.  
  614.  
  615.  
  616. <h3 class="wp-block-heading">Auto-Magical WordPress Info</h3>
  617.  
  618.  
  619.  
  620. <p>Want to add WordPress-specific details to all your logs automatically? Here&#8217;s how:</p>
  621.  
  622.  
  623.  
  624. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">add_filter('wonolog.processor-factory', function($factory) {
  625.    // Create a processor that adds WP info
  626.    $factory-&gt;addCustomFactory('wp_debug_info', function() {
  627.        return function(array $record) {
  628.            // Add useful WordPress details
  629.            $record['extra']['wp_version'] = get_bloginfo('version');
  630.            $record['extra']['theme'] = wp_get_theme()-&gt;get('Name');
  631.            $record['extra']['active_plugins'] = count(get_option('active_plugins'));
  632.            $record['extra']['memory_used'] = size_format(memory_get_usage());
  633.            
  634.            return $record;
  635.        };
  636.    });
  637.    return $factory;
  638. });
  639.  
  640. // Use your custom processor
  641. add_filter('wonolog.processor-record', function($processors) {
  642.    $processors[] = 'wp_debug_info';
  643.    return $processors;
  644. });</pre></div>
  645.  
  646.  
  647. <h2>Pro Tips for Debugging with Wonolog</h2>
  648. <ol>
  649. <li><strong>Dev vs Prod</strong>: Log everything in development, be picky in production</li>
  650. <li><strong>Organize with Channels</strong>: Create separate channels for different parts of your site</li>
  651. <li><strong>Context is King</strong>: Always include user IDs, post IDs, and other relevant data</li>
  652. <li><strong>Be Specific with Levels</strong>: Don&#8217;t mark everything as ERROR – be honest about severity</li>
  653. <li><strong>Log All the Things (in Development)</strong>: When building features, log liberally to understand what&#8217;s happening</li>
  654. <li><strong>Keep Production Logs Clean</strong>: Nobody wants to sift through gigabytes of debug logs</li>
  655. <li><strong>Think About Performance</strong>: In high-traffic sites, use <a href="https://github.com/inpsyde/Wonolog/blob/master/docs/05-wonolog-customization.md#buffer" target="_blank" rel="noopener">buffered logging</a> to reduce disk writes</li>
  656. </ol>
  657. <h2>Wrapping Up</h2>
  658. <p>Monolog and Wonolog might sound like fancy technical tools, but they&#8217;re really just about making your life easier as a developer. Think of them as your debugging buddies – there to help you figure out what&#8217;s going wrong and why.</p>
  659. <p>With <a href="https://seldaek.github.io/monolog/" target="_blank" rel="noopener">Monolog</a>, you get powerful, flexible logging for any PHP application. With <a href="https://inpsyde.github.io/Wonolog/" target="_blank" rel="noopener">Wonolog</a>, you get all that power in a WordPress-friendly package that just feels natural to use.</p>
  660. <p>Start adding proper logging to your projects, and you&#8217;ll wonder how you ever lived without it. Trust me – your future self will thank you when you&#8217;re trying to track down that weird bug at 2 AM!</p>
  661. <p>Want to learn more? Check out these resources:</p>
  662. <ul>
  663. <li><a href="https://github.com/Seldaek/monolog/blob/main/doc/01-usage.md" target="_blank" rel="noopener">Monolog Documentation</a></li>
  664. <li><a href="https://inpsyde.github.io/Wonolog/docs/02-basic-wonolog-concepts.html" target="_blank" rel="noopener">Wonolog Concepts</a></li>
  665. <li><a href="https://www.php-fig.org/psr/psr-3/" target="_blank" rel="noopener">PSR-3 Logger Interface</a></li>
  666. <li><a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank" rel="noopener">WordPress Debugging Guide</a></li>
  667. </ul>
  668. <p>Now go forth and log all the things (but maybe not in production)!</p>
  669.  
  670.  
  671.  
  672. ]]></content:encoded>
  673. <wfw:commentRss>https://portalzine.de/making-your-life-easier-debugging-with-monolog-and-wonolog-in-wordpress/feed/</wfw:commentRss>
  674. <slash:comments>0</slash:comments>
  675. </item>
  676. <item>
  677. <title>Day 29: Bugsink – Self-hosted Error Tracking – 7 Days of Docker</title>
  678. <link>https://portalzine.de/day-29-bugsink-self-hosted-error-tracking-7-days-of-docker/</link>
  679. <comments>https://portalzine.de/day-29-bugsink-self-hosted-error-tracking-7-days-of-docker/#respond</comments>
  680. <dc:creator><![CDATA[]]></dc:creator>
  681. <pubDate>Wed, 23 Apr 2025 11:34:24 +0000</pubDate>
  682. <category><![CDATA[Development]]></category>
  683. <category><![CDATA[Deployment]]></category>
  684. <category><![CDATA[Docker]]></category>
  685. <category><![CDATA[Hosting]]></category>
  686. <category><![CDATA[Series Docker]]></category>
  687. <guid isPermaLink="false">https://portalzine.de/?p=8441</guid>
  688.  
  689. <description><![CDATA[For the past several years, my error tracking journey has involved multiple platforms. Initially, I utilized Sentry in both their hosted service and self-hosted configurations. However, as Sentry&#8217;s pricing structure became prohibitively expensive and maintaining the self-hosted version proved resource-intensive. I transitioned to Glitchtip (Article about setting it up) approximately four years ago. Glitchtip has [&#8230;]]]></description>
  690. <content:encoded><![CDATA[
  691. <p>For the past several years, my error tracking journey has involved multiple platforms. Initially, I utilized <a href="https://sentry.io/" target="_blank" rel="noopener">Sentry</a> in both their hosted service and self-hosted configurations. </p>
  692.  
  693.  
  694.  
  695. <p>However, as Sentry&#8217;s pricing structure became prohibitively expensive and maintaining the self-hosted version proved resource-intensive.</p>
  696.  
  697.  
  698.  
  699. <p>I transitioned to Glitchtip (<a href="https://portalzine.de/day-3-glitchtip-7-days-of-docker/">Article about setting it up</a>) approximately four years ago. Glitchtip has performed reliably overall, with only occasional REDIS-related crashes disrupting service. </p>
  700.  
  701.  
  702.  
  703. <p>Recently, I&#8217;ve become aware of <a href="https://www.bugsink.com/" target="_blank" rel="noopener">Bugsink</a>, an emerging competitor in the self-hosted error tracking space. They also offer a hosted service, at <a href="https://www.bugsink.com/#pricing" target="_blank" rel="noopener">competitive prices</a>. First impressions are solid, slim solution that concentrates on bug tracking &#8230; thank you!</p>
  704.  
  705.  
  706.  
  707. <h2 class="wp-block-heading">Prerequisites for self-hosting</h2>
  708.  
  709.  
  710.  
  711. <ul class="wp-block-list">
  712. <li>A server with <a href="https://docs.docker.com/get-docker/" target="_blank" rel="noopener">Docker</a> and <a href="https://docs.docker.com/compose/install/" target="_blank" rel="noopener">Docker Compose</a> installed</li>
  713.  
  714.  
  715.  
  716. <li>Basic knowledge of Docker and Docker Compose</li>
  717.  
  718.  
  719.  
  720. <li>Terminal access to your server</li>
  721.  
  722.  
  723.  
  724. <li>Sufficient disk space for the MySQL database and event storage</li>
  725. </ul>
  726.  
  727.  
  728.  
  729. <h2 class="wp-block-heading">Step 1: Get the Docker Compose Configuration</h2>
  730.  
  731.  
  732.  
  733. <p>1. Create a directory for your Bugsink installation:</p>
  734.  
  735.  
  736.  
  737. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">mkdir bugsink &amp;&amp; cd bugsink</pre></div>
  738.  
  739.  
  740.  
  741. <p>2. Download the sample Docker Compose file from the Bugsink repository:</p>
  742.  
  743.  
  744.  
  745. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">curl -O https://raw.githubusercontent.com/bugsink/bugsink/main/compose-sample.yaml</pre></div>
  746.  
  747.  
  748.  
  749. <p>3. Rename it to <code>compose.yaml</code>:</p>
  750.  
  751.  
  752.  
  753. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">mv compose-sample.yaml compose.yaml</pre></div>
  754.  
  755.  
  756.  
  757. <h2 class="wp-block-heading">Step 2: Configure Your compose.yaml File</h2>
  758.  
  759.  
  760.  
  761. <p>Edit the <code>compose.yaml</code> file to customize your installation. Here&#8217;s the default configuration:</p>
  762.  
  763.  
  764.  
  765. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">services:
  766.  mysql:
  767.    image: mysql:latest
  768.    restart: unless-stopped
  769.    environment:
  770.      MYSQL_ROOT_PASSWORD: change_your_passwords_for_real_usage # TODO: Change this
  771.      MYSQL_DATABASE: bugsink
  772.    volumes:
  773.      - my-datavolume:/var/lib/mysql
  774.    healthcheck:
  775.      test: ["CMD-SHELL", "exit | mysql -h localhost -P 3306 -u root -p$$MYSQL_ROOT_PASSWORD" ]
  776.      interval: 1s
  777.      timeout: 20s
  778.      retries: 30
  779.  
  780.  web:
  781.    image: bugsink/bugsink
  782.    depends_on:
  783.      mysql:
  784.        condition: service_healthy
  785.    restart: unless-stopped
  786.    ports:
  787.      - "8000:8000"
  788.    environment:
  789.      SECRET_KEY: django-insecure-RMLYThim9NybWgXiUGat32Aa0Qbgqscf4NPDQuZO2glcZPOiXn # Change this (and remove django-insecure prefix)
  790.      CREATE_SUPERUSER: admin:admin # Change this (or remove it and execute 'createsuperuser' against the running container)
  791.      PORT: 8000
  792.      DATABASE_URL: mysql://root:change_your_passwords_for_real_usage@mysql:3306/bugsink
  793.    healthcheck:
  794.      test: ["CMD-SHELL", "python -c 'import requests; requests.get(\"http://localhost:8000/\").raise_for_status()'"]
  795.      interval: 5s
  796.      timeout: 20s
  797.      retries: 10
  798.  
  799. volumes:
  800.  my-datavolume:</pre></div>
  801.  
  802.  
  803.  
  804. <h3 class="wp-block-heading">Important Security Settings to Change:</h3>
  805.  
  806.  
  807.  
  808. <ol class="wp-block-list">
  809. <li><code>MYSQL_ROOT_PASSWORD</code>: Change to a secure password</li>
  810.  
  811.  
  812.  
  813. <li><code>SECRET_KEY</code>: Generate a secure key (e.g., <code>openssl rand -base64 50</code>) and remove the &#8220;django-insecure&#8221; prefix</li>
  814.  
  815.  
  816.  
  817. <li><code>CREATE_SUPERUSER</code>: Change to your desired admin username and password</li>
  818.  
  819.  
  820.  
  821. <li>Update <code>DATABASE_URL</code> to match your MySQL password</li>
  822. </ol>
  823.  
  824.  
  825.  
  826. <h2 class="wp-block-heading">Step 3: Environment Variables by Group</h2>
  827.  
  828.  
  829.  
  830. <h3 class="wp-block-heading">Core Settings</h3>
  831.  
  832.  
  833.  
  834. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SECRET_KEY</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Secret key for cryptographic signing</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>openssl rand -base64 50</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>PORT</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Port the webserver listens on</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>8000</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>8000</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>CREATE_SUPERUSER</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Create admin user on startup</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>admin:securepassword</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>BASE_URL</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">URL where Bugsink is hosted</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>http://localhost:8000</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>https://bugsink.yourdomain.com</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SITE_TITLE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Custom site title</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>Bugsink</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>Company Bugsink</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>TIME_ZONE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Default timezone</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>UTC</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>America/New_York</code></div></td></tr></tbody></table></figure>
  835.  
  836.  
  837.  
  838. <h3 class="wp-block-heading">Database Settings</h3>
  839.  
  840.  
  841.  
  842. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>DATABASE_URL</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Database connection string</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>mysql://user:password@mysql:3306/bugsink</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MYSQL_ROOT_PASSWORD</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">MySQL root password</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>your-secure-password</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MYSQL_DATABASE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">MySQL database name</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>bugsink</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>bugsink</code></div></td></tr></tbody></table></figure>
  843.  
  844.  
  845.  
  846. <h3 class="wp-block-heading">Proxy and HTTPS Settings</h3>
  847.  
  848.  
  849.  
  850. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>BEHIND_HTTPS_PROXY</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Whether Bugsink is behind HTTPS proxy</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SECURE_PROXY_SSL_HEADER</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">SSL header configuration</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>('HTTP_X_FORWARDED_PROTO', 'https')</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SESSION_COOKIE_SECURE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Use secure cookies</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>CSRF_COOKIE_SECURE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Use secure CSRF cookies</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>USE_X_REAL_IP</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Use X-Real-IP header</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr></tbody></table></figure>
  851.  
  852.  
  853.  
  854. <h3 class="wp-block-heading">Email Settings</h3>
  855.  
  856.  
  857.  
  858. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_HOST</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">SMTP host</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>smtp.example.com</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_HOST_USER</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">SMTP username</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>user@example.com</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_HOST_PASSWORD</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">SMTP password</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>your-password</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_PORT</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">SMTP port</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>587</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>587</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_USE_TLS</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Use TLS for email</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>EMAIL_USE_SSL</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Use SSL for email</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>DEFAULT_FROM_EMAIL</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Default sender email</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>Bugsink <bugsink></bugsink></code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>Bugsink <alerts></alerts></code></div></td></tr></tbody></table></figure>
  859.  
  860.  
  861.  
  862. <h3 class="wp-block-heading">User and Team Settings</h3>
  863.  
  864.  
  865.  
  866. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SINGLE_USER</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Disable multi-user functionality</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SINGLE_TEAM</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Disable multi-team functionality</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>USER_REGISTRATION</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Control user registration</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>CB_ADMINS</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>USER_REGISTRATION_VERIFY_EMAIL</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Require email verification</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>TEAM_CREATION</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Control team creation</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>CB_ADMINS</code></div></td></tr></tbody></table></figure>
  867.  
  868.  
  869.  
  870. <h3 class="wp-block-heading">Rate Limiting and Resource Settings</h3>
  871.  
  872.  
  873.  
  874. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MAX_EVENT_SIZE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Maximum event size</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>1048576</code> (1MB)</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>2097152</code> (2MB)</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MAX_ENVELOPE_SIZE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Maximum envelope size</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>104857600</code> (100MB)</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>104857600</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MAX_EVENTS_PER_PROJECT_PER_5_MINUTES</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">5-minute rate limit</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>1000</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>5000</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>MAX_EVENTS_PER_PROJECT_PER_HOUR</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Hourly rate limit</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>5000</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>20000</code></div></td></tr></tbody></table></figure>
  875.  
  876.  
  877.  
  878. <h3 class="wp-block-heading">Event Storage Settings</h3>
  879.  
  880.  
  881.  
  882. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>FILE_EVENT_STORAGE_PATH</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Path for file event storage</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content">–</div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>/app/event_storage</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>FILE_EVENT_STORAGE_USE_FOR_WRITE</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Enable file storage</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td></tr></tbody></table></figure>
  883.  
  884.  
  885.  
  886. <h3 class="wp-block-heading">Background Worker Settings</h3>
  887.  
  888.  
  889.  
  890. <figure class="wp-block-table"><table class="mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default" class="mtr-th-tag"><div class="mtr-cell-content">Default</div></th><th data-mtr-content="Example" class="mtr-th-tag"><div class="mtr-cell-content">Example</div></th></tr></thead><tbody><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>DIGEST_IMMEDIATELY</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Process events immediately</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>TASK_ALWAYS_EAGER</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Run tasks inline</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>True</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>False</code></div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content"><code>SNAPPEA_NUM_WORKERS</code></div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Number of worker threads</div></td><td data-mtr-content="Default" class="mtr-td-tag"><div class="mtr-cell-content"><code>2</code></div></td><td data-mtr-content="Example" class="mtr-td-tag"><div class="mtr-cell-content"><code>4</code></div></td></tr></tbody></table></figure>
  891.  
  892.  
  893.  
  894. <h2 class="wp-block-heading">Step 4: Start the Bugsink Server</h2>
  895.  
  896.  
  897.  
  898. <p>1. Launch the services:</p>
  899.  
  900.  
  901.  
  902. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">docker compose up -d</pre></div>
  903.  
  904.  
  905.  
  906. <p>2. Check if the services are running:</p>
  907.  
  908.  
  909.  
  910. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">docker compose ps</pre></div>
  911.  
  912.  
  913.  
  914. <p>3. Check the logs for any issues:</p>
  915.  
  916.  
  917.  
  918. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">docker compose logs</pre></div>
  919.  
  920.  
  921.  
  922. <p>4. Access your Bugsink instance at http://your-server-ip:8000/ and log in with the credentials specified in <code>CREATE_SUPERUSER</code>.</p>
  923.  
  924.  
  925.  
  926. <h2 class="wp-block-heading">Step 5: Setting Up a Reverse Proxy for Production (Recommended)</h2>
  927.  
  928.  
  929.  
  930. <p>For production environments, it&#8217;s strongly recommended to use a reverse proxy like <a href="https://nginx.org/" target="_blank" rel="noopener">Nginx</a>. Here&#8217;s a basic Nginx configuration:</p>
  931.  
  932.  
  933.  
  934. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">server {
  935.    listen 80;
  936.    server_name bugsink.yourdomain.com;
  937.    
  938.    location / {
  939.        proxy_pass http://localhost:8000;
  940.        proxy_set_header Host $host;
  941.        proxy_set_header X-Real-IP $remote_addr;
  942.        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  943.        proxy_set_header X-Forwarded-Proto $scheme;
  944.    }
  945. }</pre></div>
  946.  
  947.  
  948.  
  949. <p>With SSL (recommended):</p>
  950.  
  951.  
  952.  
  953. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">server {
  954.    listen 443 ssl;
  955.    server_name bugsink.yourdomain.com;
  956.    
  957.    ssl_certificate /path/to/cert.pem;
  958.    ssl_certificate_key /path/to/key.pem;
  959.    
  960.    location / {
  961.        proxy_pass http://localhost:8000;
  962.        proxy_set_header Host $host;
  963.        proxy_set_header X-Real-IP $remote_addr;
  964.        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  965.        proxy_set_header X-Forwarded-Proto $scheme;
  966.    }
  967. }
  968.  
  969. server {
  970.    listen 80;
  971.    server_name bugsink.yourdomain.com;
  972.    return 301 https://$host$request_uri;
  973. }</pre></div>
  974.  
  975.  
  976.  
  977. <p>Remember to set <code>BEHIND_HTTPS_PROXY: "True"</code> in your Docker Compose configuration when using HTTPS.</p>
  978.  
  979.  
  980.  
  981. <h2 class="wp-block-heading">Step 6: Event Storage Configuration (Optional)</h2>
  982.  
  983.  
  984.  
  985. <p>By default, Bugsink stores events in the database. For larger installations, you might want to use file-based storage:</p>
  986.  
  987.  
  988.  
  989. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">environment:
  990.  # Existing variables...
  991.  
  992.  # File event storage
  993.  FILE_EVENT_STORAGE_PATH: "/app/event_storage"
  994.  FILE_EVENT_STORAGE_USE_FOR_WRITE: "True"</pre></div>
  995.  
  996.  
  997.  
  998. <p>Add a volume mount to persist the event storage:</p>
  999.  
  1000.  
  1001.  
  1002. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">volumes:
  1003.  - event-storage:/app/event_storage
  1004.  
  1005. # And add this to the volumes section
  1006. volumes:
  1007.  my-datavolume:
  1008.  event-storage:</pre></div>
  1009.  
  1010.  
  1011.  
  1012. <h2 class="wp-block-heading">Useful Links</h2>
  1013.  
  1014.  
  1015.  
  1016. <ul class="wp-block-list">
  1017. <li><a href="https://www.bugsink.com/" target="_blank" rel="noopener">Bugsink Official Website</a></li>
  1018.  
  1019.  
  1020.  
  1021. <li><a href="https://www.bugsink.com/docs/installation/" target="_blank" rel="noopener">Bugsink Documentation</a></li>
  1022.  
  1023.  
  1024.  
  1025. <li><a href="https://www.youtube.com/watch?v=dwh5sPOTn-U" target="_blank" rel="noopener">Bugsink Youtube Introduction</a></li>
  1026.  
  1027.  
  1028.  
  1029. <li><a href="https://www.bugsink.com/docs/docker-compose-install/" target="_blank" rel="noopener">Docker Compose Install Guide</a></li>
  1030.  
  1031.  
  1032.  
  1033. <li><a href="https://www.bugsink.com/docs/settings/" target="_blank" rel="noopener">Bugsink Settings Reference</a></li>
  1034.  
  1035.  
  1036.  
  1037. <li><a href="https://github.com/bugsink/bugsink" target="_blank" rel="noopener">Bugsink GitHub Repository</a></li>
  1038.  
  1039.  
  1040.  
  1041. <li><a href="https://discord.gg/bugsink" target="_blank" rel="noopener">Bugsink Discord Community</a></li>
  1042.  
  1043.  
  1044.  
  1045. <li><a href="https://docs.docker.com/" target="_blank" rel="noopener">Docker Documentation</a></li>
  1046.  
  1047.  
  1048.  
  1049. <li><a href="https://docs.docker.com/compose/" target="_blank" rel="noopener">Docker Compose Documentation</a></li>
  1050.  
  1051.  
  1052.  
  1053. <li><a href="https://nginx.org/en/docs/" target="_blank" rel="noopener">Nginx Documentation</a></li>
  1054.  
  1055.  
  1056.  
  1057. <li><a href="https://dev.mysql.com/doc/" target="_blank" rel="noopener">MySQL Documentation</a></li>
  1058. </ul>
  1059.  
  1060.  
  1061.  
  1062. <h2 class="wp-block-heading">Comparison: Sentry vs GlitchTip vs Bugsink</h2>
  1063.  
  1064.  
  1065.  
  1066. <h3 class="wp-block-heading">Overview</h3>
  1067.  
  1068.  
  1069.  
  1070. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Platform" class="mtr-th-tag"><div class="mtr-cell-content">Platform</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Philosophy" class="mtr-th-tag"><div class="mtr-cell-content">Philosophy</div></th><th data-mtr-content="Target Audience" class="mtr-th-tag"><div class="mtr-cell-content">Target Audience</div></th></tr></thead><tbody><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Sentry</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Comprehensive monitoring platform with robust features</div></td><td data-mtr-content="Philosophy" class="mtr-td-tag"><div class="mtr-cell-content">Full-featured monitoring solution beyond error tracking</div></td><td data-mtr-content="Target Audience" class="mtr-td-tag"><div class="mtr-cell-content">From small teams to large enterprises</div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">GlitchTip</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Open-source alternative compatible with Sentry SDKs</div></td><td data-mtr-content="Philosophy" class="mtr-td-tag"><div class="mtr-cell-content">Simpler, more affordable Sentry alternative</div></td><td data-mtr-content="Target Audience" class="mtr-td-tag"><div class="mtr-cell-content">Teams seeking cost-effective monitoring</div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Bugsink</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Focused error tracking with emphasis on self-hosting</div></td><td data-mtr-content="Philosophy" class="mtr-td-tag"><div class="mtr-cell-content">Streamlined, specialized error tracking</div></td><td data-mtr-content="Target Audience" class="mtr-td-tag"><div class="mtr-cell-content">Developers prioritizing simplicity, control, and predictable costs</div></td></tr></tbody></table></figure>
  1071.  
  1072.  
  1073.  
  1074. <h3 class="wp-block-heading">Feature Comparison</h3>
  1075.  
  1076.  
  1077.  
  1078. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Feature" class="mtr-th-tag"><div class="mtr-cell-content">Feature</div></th><th data-mtr-content="Sentry" class="mtr-th-tag"><div class="mtr-cell-content">Sentry</div></th><th data-mtr-content="GlitchTip" class="mtr-th-tag"><div class="mtr-cell-content">GlitchTip</div></th><th data-mtr-content="Bugsink" class="mtr-th-tag"><div class="mtr-cell-content">Bugsink</div></th></tr></thead><tbody><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Error Tracking</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Advanced)</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Compatible with Sentry)</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Core focus)</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Performance Monitoring</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Comprehensive)</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Basic)</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No (Not a focus)</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Uptime Monitoring</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Session Replay</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Profiling</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes (UI &amp; Continuous)</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Cron Monitoring</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Custom Dashboards</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Business+)</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">No</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Unlimited Projects</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Unlimited Team Members</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Team+)</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Self-hosted)</div></td></tr></tbody></table></figure>
  1079.  
  1080.  
  1081.  
  1082. <h3 class="wp-block-heading">Deployment Options</h3>
  1083.  
  1084.  
  1085.  
  1086. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Platform" class="mtr-th-tag"><div class="mtr-cell-content">Platform</div></th><th data-mtr-content="Self-Hosted" class="mtr-th-tag"><div class="mtr-cell-content">Self-Hosted</div></th><th data-mtr-content="Cloud-Hosted" class="mtr-th-tag"><div class="mtr-cell-content">Cloud-Hosted</div></th><th data-mtr-content="Installation Complexity" class="mtr-th-tag"><div class="mtr-cell-content">Installation Complexity</div></th></tr></thead><tbody><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Sentry</div></td><td data-mtr-content="Self-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Discouraged)</div></td><td data-mtr-content="Cloud-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Primary)</div></td><td data-mtr-content="Installation Complexity" class="mtr-td-tag"><div class="mtr-cell-content">Complex (Difficult &amp; Demanding)</div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">GlitchTip</div></td><td data-mtr-content="Self-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="Cloud-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="Installation Complexity" class="mtr-td-tag"><div class="mtr-cell-content">Moderate ( Pretty Easy)</div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Bugsink</div></td><td data-mtr-content="Self-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Primary)</div></td><td data-mtr-content="Cloud-Hosted" class="mtr-td-tag"><div class="mtr-cell-content">Yes (Managed)</div></td><td data-mtr-content="Installation Complexity" class="mtr-td-tag"><div class="mtr-cell-content">Simple (30-second claim)</div></td></tr></tbody></table></figure>
  1087.  
  1088.  
  1089.  
  1090. <h3 class="wp-block-heading">Pricing</h3>
  1091.  
  1092.  
  1093.  
  1094. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Platform" class="mtr-th-tag"><div class="mtr-cell-content">Platform</div></th><th data-mtr-content="Free Tier" class="mtr-th-tag"><div class="mtr-cell-content">Free Tier</div></th><th data-mtr-content="Paid Plans" class="mtr-th-tag"><div class="mtr-cell-content">Paid Plans</div></th><th data-mtr-content="Pricing Model" class="mtr-th-tag"><div class="mtr-cell-content">Pricing Model</div></th></tr></thead><tbody><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Sentry</div></td><td data-mtr-content="Free Tier" class="mtr-td-tag"><div class="mtr-cell-content">Developer: 1 user, 5K errors</div></td><td data-mtr-content="Paid Plans" class="mtr-td-tag"><div class="mtr-cell-content">• Team: $26/mo (50K errors)<br>• Business: $80/mo (50K errors)<br>• Enterprise: Custom</div></td><td data-mtr-content="Pricing Model" class="mtr-td-tag"><div class="mtr-cell-content">Managed: Event-based<br>Self-hosed: Free </div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">GlitchTip</div></td><td data-mtr-content="Free Tier" class="mtr-td-tag"><div class="mtr-cell-content">1K events/month</div></td><td data-mtr-content="Paid Plans" class="mtr-td-tag"><div class="mtr-cell-content">• Small: $15/mo (100K events)<br>• Medium: $50/mo (500K events)<br>• Large: $250/mo (3M events)</div></td><td data-mtr-content="Pricing Model" class="mtr-td-tag"><div class="mtr-cell-content">Managed:Event-based<br>Self-hosted: Free </div></td></tr><tr><td data-mtr-content="Platform" class="mtr-td-tag"><div class="mtr-cell-content">Bugsink</div></td><td data-mtr-content="Free Tier" class="mtr-td-tag"><div class="mtr-cell-content">• Self-hosted: Free<br>• Single Developer: Free (5K events)</div></td><td data-mtr-content="Paid Plans" class="mtr-td-tag"><div class="mtr-cell-content">• Team: €15/mo (50K events)<br>• Enterprise: Custom</div></td><td data-mtr-content="Pricing Model" class="mtr-td-tag"><div class="mtr-cell-content">Self-hosted: Free<br>Managed: User-based+events<br><a href="https://www.bugsink.com/blog/new-license-new-pricing/" target="_blank" rel="noopener">License Info</a></div></td></tr></tbody></table></figure>
  1095.  
  1096.  
  1097.  
  1098. <h3 class="wp-block-heading">Technical Aspects</h3>
  1099.  
  1100.  
  1101.  
  1102. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Aspect" class="mtr-th-tag"><div class="mtr-cell-content">Aspect</div></th><th data-mtr-content="Sentry" class="mtr-th-tag"><div class="mtr-cell-content">Sentry</div></th><th data-mtr-content="GlitchTip" class="mtr-th-tag"><div class="mtr-cell-content">GlitchTip</div></th><th data-mtr-content="Bugsink" class="mtr-th-tag"><div class="mtr-cell-content">Bugsink</div></th></tr></thead><tbody><tr><td data-mtr-content="Aspect" class="mtr-td-tag"><div class="mtr-cell-content">SDK Compatibility</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Native SDKs for multiple languages</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Uses Sentry’s open-source SDKs</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Compatible with Sentry SDKs</div></td></tr><tr><td data-mtr-content="Aspect" class="mtr-td-tag"><div class="mtr-cell-content">Scalability</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Enterprise-grade</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Limited information</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Claims to handle millions of events on minimal hardware</div></td></tr><tr><td data-mtr-content="Aspect" class="mtr-td-tag"><div class="mtr-cell-content">Open-Source Status</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Open-source core with commercial offerings</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Fully open-source</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Source-available proprietary</div></td></tr><tr><td data-mtr-content="Aspect" class="mtr-td-tag"><div class="mtr-cell-content">Data Control</div></td><td data-mtr-content="Sentry" class="mtr-td-tag"><div class="mtr-cell-content">Limited in cloud version</div></td><td data-mtr-content="GlitchTip" class="mtr-td-tag"><div class="mtr-cell-content">Better in self-hosted</div></td><td data-mtr-content="Bugsink" class="mtr-td-tag"><div class="mtr-cell-content">Full control with self-hosted</div></td></tr></tbody></table></figure>
  1103.  
  1104.  
  1105.  
  1106. <h3 class="wp-block-heading">Which Platform Is Right For You?</h3>
  1107.  
  1108.  
  1109.  
  1110. <h4 class="wp-block-heading">Sentry is best for:</h4>
  1111.  
  1112.  
  1113.  
  1114. <ul class="wp-block-list">
  1115. <li>Teams needing comprehensive monitoring beyond error tracking</li>
  1116.  
  1117.  
  1118.  
  1119. <li>Organizations requiring enterprise-grade features</li>
  1120.  
  1121.  
  1122.  
  1123. <li>Users who prefer a mature, fully-featured cloud solution</li>
  1124. </ul>
  1125.  
  1126.  
  1127.  
  1128. <h4 class="wp-block-heading">GlitchTip is best for:</h4>
  1129.  
  1130.  
  1131.  
  1132. <ul class="wp-block-list">
  1133. <li>Teams looking for a more affordable Sentry alternative</li>
  1134.  
  1135.  
  1136.  
  1137. <li>Organizations that value open-source software</li>
  1138.  
  1139.  
  1140.  
  1141. <li>Users who want unlimited team members at a lower price point</li>
  1142. </ul>
  1143.  
  1144.  
  1145.  
  1146. <h4 class="wp-block-heading">Bugsink is best for:</h4>
  1147.  
  1148.  
  1149.  
  1150. <ul class="wp-block-list">
  1151. <li>Developers focused specifically on error tracking</li>
  1152.  
  1153.  
  1154.  
  1155. <li>Teams wanting complete data control through self-hosting</li>
  1156.  
  1157.  
  1158.  
  1159. <li>Organizations seeking simple setup and predictable costs</li>
  1160.  
  1161.  
  1162.  
  1163. <li>Users who want to avoid event-based pricing models for self-hosted deployments</li>
  1164. </ul>
  1165.  
  1166.  
  1167.  
  1168. <h3 class="wp-block-heading">Conclusion</h3>
  1169.  
  1170.  
  1171.  
  1172. <p>When choosing an error monitoring platform, consider your specific needs, team size, budget, and technical requirements. Sentry provides a robust, feature-rich solution suitable for enterprise environments. GlitchTip offers a cost-effective, open-source alternative with balanced capabilities. Bugsink focuses on simplicity and self-hosting with predictable costs. <strong>Also see my comments at the start of the article ;)</strong></p>
  1173.  
  1174.  
  1175.  
  1176. <p>
  1177.        The ideal platform depends on your priorities: comprehensive features (Sentry), cost-effectiveness with decent features (GlitchTip), or simplicity with full control (Bugsink). Each has its strengths, making them suitable for different development environments and organizational requirements.
  1178.    </p>
  1179.  
  1180.  
  1181.  
  1182. <h3 class="wp-block-heading">Sources</h3>
  1183.  
  1184.  
  1185.  
  1186. <ul class="wp-block-list">
  1187. <li><a href="https://sentry.io/pricing/" target="_blank" rel="noopener">Sentry Pricing Page</a></li>
  1188.  
  1189.  
  1190.  
  1191. <li><a href="https://glitchtip.com/" target="_blank" rel="noopener">GlitchTip Official Website</a></li>
  1192.  
  1193.  
  1194.  
  1195. <li><a href="https://www.bugsink.com/" target="_blank" rel="noopener">Bugsink Official Website</a></li>
  1196.  
  1197.  
  1198.  
  1199. <li><a href="https://www.bugsink.com/sentry-vs-bugsink/" target="_blank" rel="noopener">Sentry vs. Bugsink Comparison</a></li>
  1200. </ul>
  1201.  
  1202.  
  1203.  
  1204.  
  1205. ]]></content:encoded>
  1206. <wfw:commentRss>https://portalzine.de/day-29-bugsink-self-hosted-error-tracking-7-days-of-docker/feed/</wfw:commentRss>
  1207. <slash:comments>0</slash:comments>
  1208. </item>
  1209. <item>
  1210. <title>Day 28: FrankenWP – FrankePHP + WordPress &#8211; 7 Days of Docker</title>
  1211. <link>https://portalzine.de/day-28-frankenwp-frankephp-wordpress-7-days-of-docker/</link>
  1212. <comments>https://portalzine.de/day-28-frankenwp-frankephp-wordpress-7-days-of-docker/#respond</comments>
  1213. <dc:creator><![CDATA[]]></dc:creator>
  1214. <pubDate>Tue, 22 Apr 2025 10:26:00 +0000</pubDate>
  1215. <category><![CDATA[Development]]></category>
  1216. <category><![CDATA[Deployment]]></category>
  1217. <category><![CDATA[Docker]]></category>
  1218. <category><![CDATA[Hosting]]></category>
  1219. <category><![CDATA[WordPress]]></category>
  1220. <category><![CDATA[Series Docker]]></category>
  1221. <guid isPermaLink="false">https://portalzine.de/?p=8437</guid>
  1222.  
  1223. <description><![CDATA[FrankenWP is a specialized WordPress Docker image built on FrankenPHP, which is a PHP application server built on top of the Caddy web server. This combination offers several advantages: This guide will walk you through setting up FrankenWP on your own server using Docker Compose, including all necessary configuration options and client connection details. Also [&#8230;]]]></description>
  1224. <content:encoded><![CDATA[
  1225. <p><a href="https://github.com/StephenMiracle/frankenwp" target="_blank" rel="noopener">FrankenWP</a> is a specialized WordPress Docker image built on <a href="https://frankenphp.dev/" target="_blank" rel="noopener">FrankenPHP</a>, which is a PHP application server built on top of the <a href="https://caddyserver.com/" target="_blank" rel="noopener">Caddy web server</a>. This combination offers several advantages:</p>
  1226.  
  1227.  
  1228.  
  1229. <ul class="wp-block-list">
  1230. <li>Single Docker image for WordPress that includes built-in distributed server caching</li>
  1231.  
  1232.  
  1233.  
  1234. <li>Uses Souin &amp; Caddy server for performance comparable to PHP-FPM + fastcgi</li>
  1235.  
  1236.  
  1237.  
  1238. <li>Simplified deployment with a single container instead of separate web server and PHP containers</li>
  1239.  
  1240.  
  1241.  
  1242. <li>Ability to mount server cache on multiple autoscaled servers</li>
  1243. </ul>
  1244.  
  1245.  
  1246.  
  1247. <p>This guide will walk you through setting up FrankenWP on your own server using Docker Compose, including all necessary configuration options and client connection details.</p>
  1248.  
  1249.  
  1250.  
  1251. <p>Also see <a href="https://portalzine.de/frankenphp-in-2025-the-zombie-app-server-that-brought-php-back-from-the-dead/">my article about FrankenPHP</a> and using it under WSL2 on Windows as a development environment.</p>
  1252.  
  1253.  
  1254.  
  1255. <h2 class="wp-block-heading">Prerequisites</h2>
  1256.  
  1257.  
  1258.  
  1259. <ul class="wp-block-list">
  1260. <li>A server with <a href="https://docs.docker.com/engine/install/" target="_blank" rel="noopener">Docker</a> and <a href="https://docs.docker.com/compose/install/" target="_blank" rel="noopener">Docker Compose</a> installed</li>
  1261.  
  1262.  
  1263.  
  1264. <li>Basic knowledge of Docker and Docker Compose</li>
  1265.  
  1266.  
  1267.  
  1268. <li>Basic understanding of <a href="https://wordpress.org/documentation/" target="_blank" rel="noopener">WordPress configuration</a></li>
  1269.  
  1270.  
  1271.  
  1272. <li>Access to your server via SSH or other remote management tools</li>
  1273. </ul>
  1274.  
  1275.  
  1276.  
  1277. <h2 class="wp-block-heading" id="setup">Setting Up the Docker Compose Environment</h2>
  1278.  
  1279.  
  1280.  
  1281. <h3 class="wp-block-heading">1. Create a Project Directory</h3>
  1282.  
  1283.  
  1284.  
  1285. <p>First, create a directory for your FrankenWP project:</p>
  1286.  
  1287.  
  1288.  
  1289. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">mkdir frankenwp-project
  1290. cd frankenwp-project</pre></div>
  1291.  
  1292.  
  1293.  
  1294. <h3 class="wp-block-heading">2. Create the Docker Compose File</h3>
  1295.  
  1296.  
  1297.  
  1298. <p>Create a <code>compose.yaml</code> file in your project directory:</p>
  1299.  
  1300.  
  1301.  
  1302. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">services:
  1303.  wordpress:
  1304.    image: stephenmiracle/frankenwp:latest
  1305.    restart: always
  1306.    ports:
  1307.      - "8094:80" # HTTP
  1308.      # - "80:80" # HTTP
  1309.      # - "443:443" # HTTPS
  1310.      # - "443:443/udp" # HTTP/3
  1311.    environment:
  1312.      SERVER_NAME: ${SERVER_NAME:-:80}
  1313.      WORDPRESS_DB_HOST: ${DB_HOST:-db}
  1314.      WORDPRESS_DB_USER: ${DB_USER:-exampleuser}
  1315.      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD:-examplepass}
  1316.      WORDPRESS_DB_NAME: ${DB_NAME:-exampledb}
  1317.      WORDPRESS_DEBUG: ${WP_DEBUG:-true}
  1318.      WORDPRESS_TABLE_PREFIX: ${DB_TABLE_PREFIX:-wp_}
  1319.      CACHE_AGE: ${CACHE_AGE:-8000}
  1320.      STATIC_CACHE_AGE: ${STATIC_CACHE_AGE:-8000}
  1321.      CADDY_GLOBAL_OPTIONS: |
  1322.        email myemail@sample.com
  1323.        auto_https disable_redirects
  1324.        debug
  1325.      # WORDPRESS_CONFIG_EXTRA: |
  1326.      #  define('WP_SITEURL', '');
  1327.      #  define('WP_HOME', '');
  1328.    volumes:
  1329.      - ./wp-content:/var/www/html/wp-content
  1330.    depends_on:
  1331.      - db
  1332.    tty: true
  1333.  
  1334.  db:
  1335.    image: mariadb:latest
  1336.    restart: always
  1337.    ports:
  1338.      - ${LOCAL_DB_PORT:-3309}:3306
  1339.    environment:
  1340.      MYSQL_DATABASE: ${DB_NAME:-exampledb}
  1341.      MYSQL_USER: ${DB_USER:-exampleuser}
  1342.      MYSQL_PASSWORD: ${DB_PASSWORD:-examplepass}
  1343.      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-examplepass}
  1344.    volumes:
  1345.      - wpdbv:/var/lib/mysql
  1346.  
  1347.  phpmyadmin:
  1348.    image: phpmyadmin/phpmyadmin
  1349.    restart: always
  1350.    ports:
  1351.      - ${LOCAL_PHPMYADMIN_PORT:-8096}:80
  1352.    environment:
  1353.      PMA_HOST: db
  1354.      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-examplepass}
  1355.    depends_on:
  1356.      - db
  1357.  
  1358. volumes:
  1359.  wpdbv:</pre></div>
  1360.  
  1361.  
  1362.  
  1363. <p>This Docker Compose file sets up three services:</p>
  1364.  
  1365.  
  1366.  
  1367. <ul class="wp-block-list">
  1368. <li><strong>wordpress</strong>: The FrankenWP container running WordPress on FrankenPHP</li>
  1369.  
  1370.  
  1371.  
  1372. <li><strong>db</strong>: A MariaDB database container</li>
  1373.  
  1374.  
  1375.  
  1376. <li><strong>phpmyadmin</strong>: A phpMyAdmin container for database management</li>
  1377. </ul>
  1378.  
  1379.  
  1380.  
  1381. <h3 class="wp-block-heading">3. Create the Environment File</h3>
  1382.  
  1383.  
  1384.  
  1385. <p>Create a <code>.env</code> file in your project directory to customize the environment variables:</p>
  1386.  
  1387.  
  1388.  
  1389. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">SERVER_NAME=:8094
  1390. WP_DEBUG=0
  1391. WORDPRESS_DB_HOST=db
  1392. WORDPRESS_DB_USER=exampleuser
  1393. WORDPRESS_DB_PASSWORD=examplepass
  1394. WORDPRESS_DB_NAME=exampledb
  1395. WORDPRESS_TABLE_PREFIX=wp_
  1396. DB_ROOT_PASSWORD=examplepass
  1397. LOCAL_DB_PORT=3306
  1398. LOCAL_PHPMYADMIN_PORT=8096</pre></div>
  1399.  
  1400.  
  1401.  
  1402. <h3 class="wp-block-heading">4. Create the wp-content Directory</h3>
  1403.  
  1404.  
  1405.  
  1406. <p>Create a <code>wp-content</code> directory in your project folder:</p>
  1407.  
  1408.  
  1409.  
  1410. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">mkdir wp-content</pre></div>
  1411.  
  1412.  
  1413.  
  1414. <p>This directory will be mounted into the WordPress container and will store themes, plugins, and uploads.</p>
  1415.  
  1416.  
  1417.  
  1418. <h2 class="wp-block-heading" id="configuration">Configuration Options</h2>
  1419.  
  1420.  
  1421.  
  1422. <h3 class="wp-block-heading" id="env-variables">Environment Variables</h3>
  1423.  
  1424.  
  1425.  
  1426. <p>The FrankenWP Docker Compose setup uses several environment variables that can be customized in your <code>.env</code> file:</p>
  1427.  
  1428.  
  1429.  
  1430. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-tr-th"><tbody><tr><th data-mtr-content="Variable" class="mtr-th-tag"><div class="mtr-cell-content">Variable</div></th><th data-mtr-content="Description" class="mtr-th-tag"><div class="mtr-cell-content">Description</div></th><th data-mtr-content="Default Value" class="mtr-th-tag"><div class="mtr-cell-content">Default Value</div></th></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">SERVER_NAME</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Caddy server name</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">:80</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WP_DEBUG</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Enable WordPress debug mode</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">true</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WORDPRESS_DB_HOST</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Database host</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">db</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WORDPRESS_DB_USER</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Database user</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">exampleuser</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WORDPRESS_DB_PASSWORD</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Database password</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">examplepass</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WORDPRESS_DB_NAME</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Database name</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">exampledb</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">WORDPRESS_TABLE_PREFIX</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">WordPress table prefix</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">wp_</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">DB_ROOT_PASSWORD</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">MySQL root password</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">examplepass</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">LOCAL_DB_PORT</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Local port for MySQL</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">3309</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">LOCAL_PHPMYADMIN_PORT</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Local port for phpMyAdmin</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">8096</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">CACHE_AGE</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Cache age in seconds</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">8000</div></td></tr><tr><td data-mtr-content="Variable" class="mtr-td-tag"><div class="mtr-cell-content">STATIC_CACHE_AGE</div></td><td data-mtr-content="Description" class="mtr-td-tag"><div class="mtr-cell-content">Static cache age in seconds</div></td><td data-mtr-content="Default Value" class="mtr-td-tag"><div class="mtr-cell-content">8000</div></td></tr></tbody></table></figure>
  1431.  
  1432.  
  1433.  
  1434. <h3 class="wp-block-heading" id="caddy-config">Caddy Server Configuration</h3>
  1435.  
  1436.  
  1437.  
  1438. <p>The FrankenWP image uses <a href="https://caddyserver.com/docs/" target="_blank" rel="noopener">Caddy server</a>, which is configured through the <code>CADDY_GLOBAL_OPTIONS</code> environment variable in the Docker Compose file. This variable allows you to set various <a href="https://caddyserver.com/docs/caddyfile/options" target="_blank" rel="noopener">Caddy options</a>:</p>
  1439.  
  1440.  
  1441.  
  1442. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">CADDY_GLOBAL_OPTIONS: |
  1443.  email myemail@sample.com
  1444.  auto_https disable_redirects
  1445.  debug</pre></div>
  1446.  
  1447.  
  1448.  
  1449. <p>Common Caddy options include:</p>
  1450.  
  1451.  
  1452.  
  1453. <ul class="wp-block-list">
  1454. <li><code>email</code>: Email address for Let&#8217;s Encrypt certificate</li>
  1455.  
  1456.  
  1457.  
  1458. <li><code>auto_https</code>: HTTPS configuration (disable_redirects turns off HTTP to HTTPS redirects)</li>
  1459.  
  1460.  
  1461.  
  1462. <li><code>debug</code>: Enable debug mode</li>
  1463.  
  1464.  
  1465.  
  1466. <li><code>frankenphp</code>: FrankenPHP configuration options</li>
  1467. </ul>
  1468.  
  1469.  
  1470.  
  1471. <p>For production use, you should enable HTTPS by updating the <code>CADDY_GLOBAL_OPTIONS</code> and uncommenting the HTTPS port mapping in the Docker Compose file.</p>
  1472.  
  1473.  
  1474.  
  1475. <h3 class="wp-block-heading" id="wp-config">WordPress Configuration</h3>
  1476.  
  1477.  
  1478.  
  1479. <p>Additional <a href="https://wordpress.org/documentation/article/editing-wp-config-php/" target="_blank" rel="noopener">WordPress configuration</a> can be added using the <code>WORDPRESS_CONFIG_EXTRA</code> environment variable:</p>
  1480.  
  1481.  
  1482.  
  1483. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">WORDPRESS_CONFIG_EXTRA: |
  1484.  define('WP_SITEURL', 'https://example.com');
  1485.  define('WP_HOME', 'https://example.com');</pre></div>
  1486.  
  1487.  
  1488.  
  1489. <h2 class="wp-block-heading" id="running">Running the FrankenWP Server</h2>
  1490.  
  1491.  
  1492.  
  1493. <h3 class="wp-block-heading">1. Start the Containers</h3>
  1494.  
  1495.  
  1496.  
  1497. <p>From your project directory, run:</p>
  1498.  
  1499.  
  1500.  
  1501. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">docker compose up -d</pre></div>
  1502.  
  1503.  
  1504.  
  1505. <p>This will start all three containers in detached mode.</p>
  1506.  
  1507.  
  1508.  
  1509. <h3 class="wp-block-heading">2. Check the Status</h3>
  1510.  
  1511.  
  1512.  
  1513. <p>Verify that all containers are running properly:</p>
  1514.  
  1515.  
  1516.  
  1517. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">docker compose ps</pre></div>
  1518.  
  1519.  
  1520.  
  1521. <h3 class="wp-block-heading">3. Access WordPress</h3>
  1522.  
  1523.  
  1524.  
  1525. <p>Once the containers are running, you can access WordPress at:</p>
  1526.  
  1527.  
  1528.  
  1529. <ul class="wp-block-list">
  1530. <li>WordPress: http://your-server-ip:8094</li>
  1531.  
  1532.  
  1533.  
  1534. <li>phpMyAdmin: http://your-server-ip:8096</li>
  1535. </ul>
  1536.  
  1537.  
  1538.  
  1539. <h3 class="wp-block-heading">4. Complete the WordPress Setup</h3>
  1540.  
  1541.  
  1542.  
  1543. <p>Visit http://your-server-ip:8094 in your browser to complete the WordPress installation process.</p>
  1544.  
  1545.  
  1546.  
  1547. <h2 class="wp-block-heading" id="connecting">Connecting to the Server</h2>
  1548.  
  1549.  
  1550.  
  1551. <h3 class="wp-block-heading">1. Browser Connection</h3>
  1552.  
  1553.  
  1554.  
  1555. <p>To connect to your FrankenWP server, simply point your web browser to:</p>
  1556.  
  1557.  
  1558.  
  1559. <ul class="wp-block-list">
  1560. <li>http://your-server-ip:8094 (or whichever port you configured)</li>
  1561. </ul>
  1562.  
  1563.  
  1564.  
  1565. <h3 class="wp-block-heading">2. WordPress Admin</h3>
  1566.  
  1567.  
  1568.  
  1569. <p>Once WordPress is set up, you can access the admin panel at:</p>
  1570.  
  1571.  
  1572.  
  1573. <ul class="wp-block-list">
  1574. <li>http://your-server-ip:8094/wp-admin</li>
  1575. </ul>
  1576.  
  1577.  
  1578.  
  1579. <h3 class="wp-block-heading" id="domain-config">3. Domain Configuration</h3>
  1580.  
  1581.  
  1582.  
  1583. <p>For production use, you&#8217;ll want to use a domain name instead of an IP address. Update your <a href="https://www.cloudflare.com/learning/dns/what-is-dns/" target="_blank" rel="noopener">DNS settings</a> to point your domain to your server&#8217;s IP address.</p>
  1584.  
  1585.  
  1586.  
  1587. <p>Then, update the Docker Compose file to use your domain:</p>
  1588.  
  1589.  
  1590.  
  1591. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">environment:
  1592.  SERVER_NAME: yourdomain.com
  1593.  # Other environment variables...</pre></div>
  1594.  
  1595.  
  1596.  
  1597. <p>And uncomment the HTTPS port mappings:</p>
  1598.  
  1599.  
  1600.  
  1601. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">ports:
  1602.  # - "8094:80" # HTTP (development)
  1603.  - "80:80" # HTTP
  1604.  - "443:443" # HTTPS
  1605.  - "443:443/udp" # HTTP/3</pre></div>
  1606.  
  1607.  
  1608.  
  1609. <p>Update the Caddy configuration to enable automatic HTTPS:</p>
  1610.  
  1611.  
  1612.  
  1613. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">CADDY_GLOBAL_OPTIONS: |
  1614.  email youremail@yourdomain.com
  1615.  # Remove or comment out auto_https disable_redirects</pre></div>
  1616.  
  1617.  
  1618.  
  1619. <h2 class="wp-block-heading" id="advanced">Advanced Configuration</h2>
  1620.  
  1621.  
  1622.  
  1623. <h3 class="wp-block-heading" id="frankephp-settings">Customizing FrankenPHP Settings</h3>
  1624.  
  1625.  
  1626.  
  1627. <p><a href="https://frankenphp.dev/docs/config/" target="_blank" rel="noopener">FrankenPHP settings</a> can be customized in the Caddy configuration. For example, to adjust PHP worker threads:</p>
  1628.  
  1629.  
  1630.  
  1631. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">CADDY_GLOBAL_OPTIONS: |
  1632.  email youremail@yourdomain.com
  1633.  frankenphp {
  1634.    num_threads 16  # Sets the number of PHP threads to start
  1635.    max_threads 32  # Limits the number of additional PHP threads
  1636.    php_ini upload_max_filesize 100M  # Set PHP configuration
  1637.    php_ini post_max_size 100M
  1638.  }</pre></div>
  1639.  
  1640.  
  1641.  
  1642. <h3 class="wp-block-heading" id="custom-caddyfile">Custom Caddyfile</h3>
  1643.  
  1644.  
  1645.  
  1646. <p>For more complex <a href="https://caddyserver.com/docs/caddyfile" target="_blank" rel="noopener">Caddy configurations</a>, you can mount a custom Caddyfile into the container:</p>
  1647.  
  1648.  
  1649.  
  1650. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">services:
  1651.  wordpress:
  1652.    # Other settings...
  1653.    volumes:
  1654.      - ./wp-content:/var/www/html/wp-content
  1655.      - ./Caddyfile:/etc/caddy/Caddyfile</pre></div>
  1656.  
  1657.  
  1658.  
  1659. <h3 class="wp-block-heading" id="ssl-tls">SSL/TLS Configuration</h3>
  1660.  
  1661.  
  1662.  
  1663. <p>Caddy automatically handles <a href="https://caddyserver.com/docs/automatic-https" target="_blank" rel="noopener">SSL/TLS certificates</a> using <a href="https://letsencrypt.org/" target="_blank" rel="noopener">Let&#8217;s Encrypt</a> when you configure a domain name. In production, enable HTTPS by updating your Docker Compose file:</p>
  1664.  
  1665.  
  1666.  
  1667. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">ports:
  1668.  - "80:80"
  1669.  - "443:443"
  1670.  - "443:443/udp"  # HTTP/3
  1671. environment:
  1672.  SERVER_NAME: yourdomain.com
  1673.  CADDY_GLOBAL_OPTIONS: |
  1674.    email youremail@yourdomain.com</pre></div>
  1675.  
  1676.  
  1677.  
  1678. <h2 class="wp-block-heading" id="references">References</h2>
  1679.  
  1680.  
  1681.  
  1682. <ul class="wp-block-list">
  1683. <li><a href="https://github.com/StephenMiracle/wordpress-docker-frankenphp" target="_blank" rel="noopener">StephenMiracle/wordpress-docker-frankenphp on GitHub</a></li>
  1684.  
  1685.  
  1686.  
  1687. <li><a href="https://gist.github.com/StephenMiracle/a16f5bee113d2a0d6b68c5e1787c0066" target="_blank" rel="noopener">FrankenWP Docker Compose Sample</a></li>
  1688.  
  1689.  
  1690.  
  1691. <li><a href="https://frankenphp.dev/docs/" target="_blank" rel="noopener">FrankenPHP Documentation</a></li>
  1692.  
  1693.  
  1694.  
  1695. <li><a href="https://caddyserver.com/docs/" target="_blank" rel="noopener">Caddy Server Documentation</a></li>
  1696.  
  1697.  
  1698.  
  1699. <li><a href="https://docs.docker.com/compose/compose-file/" target="_blank" rel="noopener">Docker Compose Documentation</a></li>
  1700.  
  1701.  
  1702.  
  1703. <li><a href="https://wordpress.org/documentation/" target="_blank" rel="noopener">WordPress Documentation</a></li>
  1704.  
  1705.  
  1706.  
  1707. <li><a href="https://wpeverywhere.com/introducing-the-frankenwp-docker-image/" target="_blank" rel="noopener">WP Everywhere &#8211; Introducing FrankenWP</a></li>
  1708. </ul>
  1709. ]]></content:encoded>
  1710. <wfw:commentRss>https://portalzine.de/day-28-frankenwp-frankephp-wordpress-7-days-of-docker/feed/</wfw:commentRss>
  1711. <slash:comments>0</slash:comments>
  1712. </item>
  1713. <item>
  1714. <title>FrankenPHP in 2025: The Zombie App Server That Brought PHP Back From the Dead!</title>
  1715. <link>https://portalzine.de/frankenphp-in-2025-the-zombie-app-server-that-brought-php-back-from-the-dead/</link>
  1716. <comments>https://portalzine.de/frankenphp-in-2025-the-zombie-app-server-that-brought-php-back-from-the-dead/#respond</comments>
  1717. <dc:creator><![CDATA[]]></dc:creator>
  1718. <pubDate>Sat, 19 Apr 2025 08:08:10 +0000</pubDate>
  1719. <category><![CDATA[Development]]></category>
  1720. <category><![CDATA[Automation]]></category>
  1721. <category><![CDATA[Build-Tools]]></category>
  1722. <category><![CDATA[Debugging]]></category>
  1723. <category><![CDATA[Deployment]]></category>
  1724. <category><![CDATA[Enviroment]]></category>
  1725. <category><![CDATA[Local]]></category>
  1726. <category><![CDATA[PHP]]></category>
  1727. <guid isPermaLink="false">https://portalzine.de/?p=8420</guid>
  1728.  
  1729. <description><![CDATA[Remember when people used to joke that PHP was dying? Well, in 2025, PHP is not only alive and kicking but thriving thanks to its Frankenstein-inspired application server that&#8217;s been taking the web development world by storm! What Is This Monster? FrankenPHP is the brainchild of Kévin Dunglas (the same genius behind API Platform) who [&#8230;]]]></description>
  1730. <content:encoded><![CDATA[
  1731. <p>Remember when people used to joke that PHP was dying? Well, in 2025, PHP is not only alive and kicking but thriving thanks to its Frankenstein-inspired application server that&#8217;s been taking the web development world by storm!</p>
  1732.  
  1733.  
  1734.  
  1735. <h2 class="wp-block-heading">What Is This Monster?</h2>
  1736.  
  1737.  
  1738.  
  1739. <p><a href="https://frankenphp.dev/" target="_blank" rel="noopener">FrankenPHP</a> is the brainchild of Kévin Dunglas (the same genius behind API Platform) who first unleashed this creation in October 2022. It&#8217;s a PHP application server built on top of the <a href="https://caddyserver.com/" target="_blank" rel="noopener">Caddy web server</a> and written in <a href="https://go.dev/" target="_blank" rel="noopener">Go</a>. </p>
  1740.  
  1741.  
  1742.  
  1743. <p>Think of it as the cool science experiment that combined the best parts of different technologies to create something even more powerful than the sum of its parts.</p>
  1744.  
  1745.  
  1746.  
  1747. <p>Unlike the monster from Mary Shelley&#8217;s novel, this creation is extremely friendly and incredibly helpful!</p>
  1748.  
  1749.  
  1750.  
  1751. <h2 class="wp-block-heading">Why Developers Are Obsessed With It in 2025</h2>
  1752.  
  1753.  
  1754.  
  1755. <p>Three years after its major 1.3 <a href="https://github.com/dunglas/frankenphp/releases" target="_blank" rel="noopener">release</a> (which delivered a whopping 54% performance boost), FrankenPHP has become the go-to solution for PHP developers who want speed, reliability, and modern features.</p>
  1756.  
  1757.  
  1758.  
  1759. <p>The server landscape in 2025 has drastically changed. What used to be a complicated dance of Nginx/Apache configurations, PHP-FPM setups, and various proxy layers has been simplified to &#8220;just use FrankenPHP&#8221; in many PHP shops.</p>
  1760.  
  1761.  
  1762.  
  1763. <p>&#8220;I literally deleted 200 lines of server config and replaced it with 10 lines of FrankenPHP config,&#8221; says one happy developer on Reddit&#8217;s r/PHP. &#8220;It&#8217;s like I finally got my weekends back!&#8221;</p>
  1764.  
  1765.  
  1766.  
  1767. <h2 class="wp-block-heading">The Secret Sauce: What Makes It Special</h2>
  1768.  
  1769.  
  1770.  
  1771. <p>FrankenPHP&#8217;s popularity in 2025 comes down to a few key ingredients:</p>
  1772.  
  1773.  
  1774.  
  1775. <ul class="wp-block-list">
  1776. <li><strong>Worker Mode</strong>: Applications run lightning-fast because code stays loaded in memory</li>
  1777.  
  1778.  
  1779.  
  1780. <li><strong>Real-time Capabilities</strong>: Built-in Mercure hub for instant updates (no more polling!)</li>
  1781.  
  1782.  
  1783.  
  1784. <li><strong>Developer Experience</strong>: The file watcher feature means you can edit code and just refresh your browser</li>
  1785.  
  1786.  
  1787.  
  1788. <li><strong>Modern By Default</strong>: Automatic HTTPS, HTTP/2, and HTTP/3 support without painful configuration</li>
  1789.  
  1790.  
  1791.  
  1792. <li><strong>Container-Friendly</strong>: The deployment darling for DevOps teams everywhere</li>
  1793. </ul>
  1794.  
  1795.  
  1796.  
  1797. <p>One feature that&#8217;s been a game-changer in 2025: the advanced metrics integration that&#8217;s made it super easy for teams to monitor and optimize their applications. Operations teams are particularly fond of the granular control over thread and worker management.</p>
  1798.  
  1799.  
  1800.  
  1801. <h2 class="wp-block-heading">The Community Effect</h2>
  1802.  
  1803.  
  1804.  
  1805. <p>The <a href="https://api-platform.com/con/2025/" target="_blank" rel="noopener">API Platform Conference 2025</a> happening September 18-19 in Lille, France has an entire track dedicated to FrankenPHP—no surprise considering how popular it&#8217;s become. The conference brings together FrankenPHP creators, contributors, and enthusiastic users sharing their success stories.</p>
  1806.  
  1807.  
  1808.  
  1809. <p>What&#8217;s fascinating is how FrankenPHP has bridged communities: PHP devs learning Go basics to contribute, and Go developers dipping their toes into PHP waters to enhance the server. It&#8217;s created this weird but wonderful cross-pollination that&#8217;s energized both communities.</p>
  1810.  
  1811.  
  1812.  
  1813. <h2 class="wp-block-heading">Real Talk: Is It Perfect?</h2>
  1814.  
  1815.  
  1816.  
  1817. <p>Of course not! But that&#8217;s part of its charm. The FrankenPHP Discord server has this legendary channel called #franken-fails where developers share their most spectacular configuration disasters—often followed by the simple fix that made everything work.</p>
  1818.  
  1819.  
  1820.  
  1821. <p>&#8220;I somehow managed to crash my entire cluster with one typo,&#8221; shared one developer at a recent meetup. &#8220;But the error message was so clear I had it fixed before my boss noticed!&#8221;</p>
  1822.  
  1823.  
  1824.  
  1825. <h2 class="wp-block-heading">What&#8217;s Next?</h2>
  1826.  
  1827.  
  1828.  
  1829. <p>Rumor has it that the FrankenPHP team is working on even deeper integration with major frameworks and improved observability features. There&#8217;s also talk of experimental machine learning capabilities to automatically optimize server settings based on your application&#8217;s patterns.</p>
  1830.  
  1831.  
  1832.  
  1833. <p>While we wait for the next big update, developers continue finding creative ways to use this &#8220;monster&#8221; server. From ultra-lightweight microservices to mammoth enterprise applications, FrankenPHP has proven itself versatile enough to handle just about anything thrown at it.</p>
  1834.  
  1835.  
  1836.  
  1837. <h2 class="wp-block-heading">The Bottom Line</h2>
  1838.  
  1839.  
  1840.  
  1841. <p>In 2025, if you&#8217;re still running PHP without FrankenPHP, your colleagues are probably wondering why. It&#8217;s become as standard to PHP development as composer and curly braces.</p>
  1842.  
  1843.  
  1844.  
  1845. <p>As one conference speaker recently put it: &#8220;FrankenPHP didn&#8217;t just improve PHP deployment—it brought the fun back to server management.&#8221; And in the sometimes tedious world of server configuration, that might be its greatest achievement yet!</p>
  1846.  
  1847.  
  1848.  
  1849. <h2 class="wp-block-heading">Links &amp; Resources</h2>
  1850.  
  1851.  
  1852.  
  1853. <h3 class="wp-block-heading">Official Resources</h3>
  1854.  
  1855.  
  1856.  
  1857. <ul class="wp-block-list">
  1858. <li><strong>Main Website</strong>: <a href="https://frankenphp.dev/" target="_blank" rel="noopener">frankenphp.dev</a></li>
  1859.  
  1860.  
  1861.  
  1862. <li><strong>Documentation</strong>: <a href="https://frankenphp.dev/docs/" target="_blank" rel="noopener">frankenphp.dev/docs</a></li>
  1863.  
  1864.  
  1865.  
  1866. <li><strong>GitHub Repository</strong>: <a href="https://github.com/dunglas/frankenphp" target="_blank" rel="noopener">github.com/dunglas/frankenphp</a></li>
  1867.  
  1868.  
  1869.  
  1870. <li><strong>GitHub Discussions</strong> (official community forum): <a href="https://github.com/dunglas/frankenphp/discussions" target="_blank" rel="noopener">github.com/dunglas/frankenphp/discussions</a></li>
  1871.  
  1872.  
  1873.  
  1874. <li><a href="https://www.youtube.com/watch?v=nuxI6MSEMEg" target="_blank" rel="noopener">PHP UK Conference &#8211; Youtube Presentation</a></li>
  1875. </ul>
  1876.  
  1877.  
  1878.  
  1879. <h3 class="wp-block-heading">Community Resources</h3>
  1880.  
  1881.  
  1882.  
  1883. <ul class="wp-block-list">
  1884. <li><strong>No Official Discord</strong>: The maintainers explicitly prefer GitHub Discussions over Discord for better searchability and organization (confirmed in <a href="https://github.com/dunglas/frankenphp/issues/823" target="_blank" rel="noopener">GitHub issue #823</a>)</li>
  1885.  
  1886.  
  1887.  
  1888. <li><strong>GitHub Issues</strong>: <a href="https://github.com/dunglas/frankenphp/issues" target="_blank" rel="noopener">github.com/dunglas/frankenphp/issues</a></li>
  1889.  
  1890.  
  1891.  
  1892. <li><strong>API Platform Conference</strong>: Annual event with FrankenPHP creators and contributors (Next: Sep 18-19, 2025 in Lille, France)</li>
  1893. </ul>
  1894.  
  1895.  
  1896.  
  1897. <h3 class="wp-block-heading">Creator Information</h3>
  1898.  
  1899.  
  1900.  
  1901. <ul class="wp-block-list">
  1902. <li><strong>Kévin Dunglas</strong>: Creator of FrankenPHP, Symfony Core Team member</li>
  1903.  
  1904.  
  1905.  
  1906. <li><strong>Twitter/X</strong>: <a href="https://twitter.com/dunglas" target="_blank" rel="noopener">@dunglas</a></li>
  1907.  
  1908.  
  1909.  
  1910. <li><strong>Mastodon</strong>: <a href="https://mastodon.social/@dunglas" target="_blank" rel="noopener">@dunglas@mastodon.social</a></li>
  1911.  
  1912.  
  1913.  
  1914. <li><strong>GitHub</strong>: <a href="https://github.com/dunglas" target="_blank" rel="noopener">github.com/dunglas</a></li>
  1915.  
  1916.  
  1917.  
  1918. <li><strong>Website</strong>: <a href="https://dunglas.dev/" target="_blank" rel="noopener">dunglas.dev</a></li>
  1919. </ul>
  1920.  
  1921.  
  1922.  
  1923. <h2 class="wp-block-heading">Bonus: Installing FrankenPHP and WordPress on Linux / WSL2</h2>
  1924.  
  1925.  
  1926.  
  1927. <p>Hey there, WordPress enthusiasts! Are you tired of sluggish WordPress performance for local development?</p>
  1928.  
  1929.  
  1930.  
  1931. <p>In this guide, I&#8217;ll walk you through setting up WordPress with FrankenPHP &#8211; it&#8217;s easier than you might think!</p>
  1932.  
  1933.  
  1934.  
  1935. <p><strong>This works perfectly on Windows <a href="https://github.com/microsoft/WSL" target="_blank" rel="noopener">WSL2</a> as well, a lot faster than most packaged solutions out there and will beat Docker on lower powered systems easily .</strong></p>
  1936.  
  1937.  
  1938.  
  1939. <p>Looking for a Docker Solution ? Check out <a href="https://github.com/StephenMiracle/frankenwp" target="_blank" rel="noopener">FrankeWP</a>.</p>
  1940.  
  1941.  
  1942.  
  1943. <h3 class="wp-block-heading">1. Getting Your System Ready</h3>
  1944.  
  1945.  
  1946.  
  1947. <p>Let&#8217;s start with the basics! First, we need to update your system (because who likes working with outdated packages, right?):</p>
  1948.  
  1949.  
  1950.  
  1951. <p><strong>Got Debian?</strong> Run this:</p>
  1952.  
  1953.  
  1954.  
  1955. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo apt update &amp;&amp; sudo apt upgrade -y</pre></div>
  1956.  
  1957.  
  1958.  
  1959. <p>Now, let&#8217;s grab some essential tools we&#8217;ll need for this project:</p>
  1960.  
  1961.  
  1962.  
  1963. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo apt install -y curl unzip git</pre></div>
  1964.  
  1965.  
  1966.  
  1967. <h3 class="wp-block-heading">2. Getting FrankenPHP Up and Running</h3>
  1968.  
  1969.  
  1970.  
  1971. <p>Now comes the exciting part &#8211; installing FrankenPHP! Just copy-paste these commands and you&#8217;ll be all set:</p>
  1972.  
  1973.  
  1974.  
  1975. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">curl https://frankenphp.dev/install.sh | sh
  1976. mv frankenphp /usr/local/bin/</pre></div>
  1977.  
  1978.  
  1979.  
  1980. <p>Let&#8217;s make sure it worked (fingers crossed):</p>
  1981.  
  1982.  
  1983.  
  1984. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">frankenphp -v</pre></div>
  1985.  
  1986.  
  1987.  
  1988. <h3 class="wp-block-heading">3. Getting WordPress Ready</h3>
  1989.  
  1990.  
  1991.  
  1992. <p>Time to grab WordPress! Let&#8217;s download and unpack it:</p>
  1993.  
  1994.  
  1995.  
  1996. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">curl -O https://wordpress.org/latest.zip
  1997. unzip latest.zip
  1998. mv wordpress /var/www/wordpress
  1999. cd /var/www/wordpress</pre></div>
  2000.  
  2001.  
  2002.  
  2003. <p>Now we need to set some permissions (boring but necessary stuff):</p>
  2004.  
  2005.  
  2006.  
  2007. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo chown -R www-data:www-data /var/www/wordpress
  2008. sudo chmod -R 755 /var/www/wordpress</pre></div>
  2009.  
  2010.  
  2011.  
  2012. <h3 class="wp-block-heading">4. Lets go FrankenPHP</h3>
  2013.  
  2014.  
  2015.  
  2016. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">frankenphp php-server --listen :9000 /var/www/wordpress</pre></div>
  2017.  
  2018.  
  2019.  
  2020. <p>Boom! Your WordPress is now running on port 8080. Open your browser and go to http://server-ip:8080 to see the magic happening!</p>
  2021.  
  2022.  
  2023.  
  2024. <h3 class="wp-block-heading">5. Setting Up the Database</h3>
  2025.  
  2026.  
  2027.  
  2028. <p>WordPress needs a database to store all your awesome content. Let&#8217;s set up MariaDB:</p>
  2029.  
  2030.  
  2031.  
  2032. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo apt-get install mariadb -y</pre></div>
  2033.  
  2034.  
  2035.  
  2036. <p>Let&#8217;s start MariaDB and make sure it runs at startup (because who wants to manually start it every time, right?):</p>
  2037.  
  2038.  
  2039.  
  2040. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo systemctl enable --now mariadb</pre></div>
  2041.  
  2042.  
  2043.  
  2044. <p>Time to create a database for WordPress! Log in to MySQL (you&#8217;ll need to enter your password):</p>
  2045.  
  2046.  
  2047.  
  2048. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo mysql -u root -p</pre></div>
  2049.  
  2050.  
  2051.  
  2052. <p>Once you&#8217;re in, run these SQL commands (make sure to change that &#8216;strongpassword&#8217; to something actually strong!):</p>
  2053.  
  2054.  
  2055.  
  2056. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">CREATE DATABASE wordpress;
  2057. CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'strongpassword';
  2058. GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
  2059. FLUSH PRIVILEGES;
  2060. EXIT;</pre></div>
  2061.  
  2062.  
  2063.  
  2064. <h3 class="wp-block-heading">6. Wrapping Up the WordPress Setup</h3>
  2065.  
  2066.  
  2067.  
  2068. <p>Almost done! Fire up your browser and go to http://server-ip:8080. You&#8217;ll see the famous WordPress setup wizard. Just enter the database details we created earlier, and you&#8217;re good to go!</p>
  2069.  
  2070.  
  2071.  
  2072. <h4 class="wp-block-heading">Start on Boot</h4>
  2073.  
  2074.  
  2075.  
  2076. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo nano /etc/systemd/system/frankenphp.service</pre></div>
  2077.  
  2078.  
  2079.  
  2080. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">[Unit]
  2081. Description=FrankenPHP service
  2082. After=network.target
  2083. StartLimitIntervalSec=0
  2084. [Service]
  2085. Type=simple
  2086. Restart=always
  2087. RestartSec=1
  2088. User=YOURUSER
  2089. WorkingDirectory=/home/YOURUSER
  2090. ExecStart=frankenphp php-server --listen :9000 /var/www/wordpress
  2091.  
  2092. [Install]
  2093. WantedBy=multi-user.target</pre></div>
  2094.  
  2095.  
  2096.  
  2097. <h4 class="wp-block-heading">Start Service</h4>
  2098.  
  2099.  
  2100.  
  2101. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">systemctl start frankenphp</pre></div>
  2102.  
  2103.  
  2104.  
  2105. <h4 class="wp-block-heading">Check Status</h4>
  2106.  
  2107.  
  2108.  
  2109. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">systemctl status frankenphp</pre></div>
  2110.  
  2111.  
  2112.  
  2113. <h4 class="wp-block-heading">Autostart</h4>
  2114.  
  2115.  
  2116.  
  2117. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">systemctl enable frankenphp</pre></div>
  2118.  
  2119.  
  2120.  
  2121. <h4 class="wp-block-heading">PHP Tweaks</h4>
  2122.  
  2123.  
  2124.  
  2125. <p><a href="https://github.com/laravel/docs/pull/9932/files#r1789864795" target="_blank" rel="noopener">Create</a> a <strong>php.ini</strong> in <strong>/lib</strong></p>
  2126.  
  2127.  
  2128.  
  2129. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">memory_limit = 256M
  2130. max_execution_time = 120
  2131. display_errors = Off
  2132.  
  2133. zend_extension=opcache
  2134. opcache.enable=1
  2135.  
  2136. upload_max_filesize = 64M
  2137.  
  2138. zlib.output_compression = On
  2139. zlib.output_compression_level = 5
  2140.  
  2141. realpath_cache_size = 16M
  2142. realpath_cache_ttl = 120
  2143.  
  2144. session.save_handler = files
  2145. session.save_path = /var/lib/php/sessions
  2146. session.cache_limiter = public
  2147. session.cache_expire = 180</pre></div>
  2148.  
  2149.  
  2150.  
  2151. <h3 class="wp-block-heading">That&#8217;s It &#8211; You&#8217;re Done!</h3>
  2152.  
  2153.  
  2154.  
  2155. <p>Congrats! You&#8217;ve just set up WordPress using FrankenPHP, which means your site should be faster and more efficient than a regular WordPress installation. Pretty cool, right?</p>
  2156.  
  2157.  
  2158.  
  2159. <p>I hope this guide helped you get your super-fast WordPress site up and running! Now go create some awesome content and enjoy those faster load times. Your visitors (and Google) will thank you!</p>
  2160. ]]></content:encoded>
  2161. <wfw:commentRss>https://portalzine.de/frankenphp-in-2025-the-zombie-app-server-that-brought-php-back-from-the-dead/feed/</wfw:commentRss>
  2162. <slash:comments>0</slash:comments>
  2163. </item>
  2164. <item>
  2165. <title>The Ultimate Guide to Presence Detection in JavaScript: What Works and Why</title>
  2166. <link>https://portalzine.de/the-ultimate-guide-to-presence-detection-in-javascript-what-works-and-why/</link>
  2167. <comments>https://portalzine.de/the-ultimate-guide-to-presence-detection-in-javascript-what-works-and-why/#respond</comments>
  2168. <dc:creator><![CDATA[]]></dc:creator>
  2169. <pubDate>Mon, 14 Apr 2025 19:56:10 +0000</pubDate>
  2170. <category><![CDATA[Development]]></category>
  2171. <category><![CDATA[API]]></category>
  2172. <category><![CDATA[Javascript]]></category>
  2173. <guid isPermaLink="false">https://portalzine.de/?p=8099</guid>
  2174.  
  2175. <description><![CDATA[Hey there! Ever wondered how websites know when you&#8217;re actually looking at them, or if you&#8217;ve wandered off to make coffee? That&#8217;s presence detection in action &#8211; and it&#8217;s super useful for creating responsive, user-friendly web apps. In this guide, I&#8217;ll walk you through everything you need to know about detecting user presence with JavaScript [&#8230;]]]></description>
  2176. <content:encoded><![CDATA[
  2177. <p>Hey there! Ever wondered how websites know when you&#8217;re actually looking at them, or if you&#8217;ve wandered off to make coffee? That&#8217;s presence detection in action &#8211; and it&#8217;s super useful for creating responsive, user-friendly web apps.</p>
  2178.  
  2179.  
  2180.  
  2181. <p>In this guide, I&#8217;ll walk you through everything you need to know about detecting user presence with JavaScript &#8211; from figuring out if someone&#8217;s still actively using your app to knowing if they&#8217;ve lost their internet connection. Let&#8217;s dive in!</p>
  2182.  
  2183.  
  2184.  
  2185. <h2 class="wp-block-heading">What&#8217;s This Presence Detection Thing All About?</h2>
  2186.  
  2187.  
  2188.  
  2189. <p>When we talk about presence detection in JavaScript, we&#8217;re really looking at four main things:</p>
  2190.  
  2191.  
  2192.  
  2193. <ol class="wp-block-list">
  2194. <li><strong>Is the user still there?</strong> (Idle/inactivity detection)</li>
  2195.  
  2196.  
  2197.  
  2198. <li><strong>Are they connected to the internet?</strong> (Online/offline status)</li>
  2199.  
  2200.  
  2201.  
  2202. <li><strong>Which users are currently active?</strong> (Real-time user presence for multi-user apps)</li>
  2203.  
  2204.  
  2205.  
  2206. <li><strong>Is our app visible or hidden?</strong> (Tab/window focus detection)</li>
  2207. </ol>
  2208.  
  2209.  
  2210.  
  2211. <p>Each of these gives us different insights into user behavior, and they&#8217;re all super useful for different reasons. Let&#8217;s break them down one by one.</p>
  2212.  
  2213.  
  2214.  
  2215. <h2 class="wp-block-heading">1. Catching Idle Users: &#8220;Hello? Anyone There?&#8221;</h2>
  2216.  
  2217.  
  2218.  
  2219. <h3 class="wp-block-heading">What&#8217;s the deal?</h3>
  2220.  
  2221.  
  2222.  
  2223. <p>This is about figuring out when someone hasn&#8217;t touched their keyboard, moved their mouse, or interacted with your app for a while. Maybe they got distracted by a cat video or went to grab lunch.</p>
  2224.  
  2225.  
  2226.  
  2227. <h3 class="wp-block-heading">Why you&#8217;d want this:</h3>
  2228.  
  2229.  
  2230.  
  2231. <ul class="wp-block-list">
  2232. <li>Auto-logout for security (so nobody messes with your banking session)</li>
  2233.  
  2234.  
  2235.  
  2236. <li>Saving battery life by pausing heavy animations</li>
  2237.  
  2238.  
  2239.  
  2240. <li>Showing others you&#8217;re &#8220;Away&#8221; in chat apps</li>
  2241.  
  2242.  
  2243.  
  2244. <li>Nudging users with &#8220;Hey, are you still there?&#8221; notifications</li>
  2245. </ul>
  2246.  
  2247.  
  2248.  
  2249. <h3 class="wp-block-heading">The simple way with vanilla JavaScript:</h3>
  2250.  
  2251.  
  2252.  
  2253. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">let inactivityTime = function() {
  2254.  let time;
  2255.  
  2256.  // Reset the timer whenever the user does something
  2257.  window.onload = resetTimer;
  2258.  document.onmousemove = resetTimer;
  2259.  document.onkeypress = resetTimer;
  2260.  document.onclick = resetTimer;
  2261.  document.ontouchstart = resetTimer;
  2262.  document.onscroll = resetTimer;
  2263.  
  2264.  function resetTimer() {
  2265.    clearTimeout(time);
  2266.    time = setTimeout(logout, 30000); // 30 seconds until we consider them idle
  2267.  }
  2268.  
  2269.  function logout() {
  2270.    console.log("Looks like they're gone...");
  2271.    // Do something - show a message, log them out, etc.
  2272.  }
  2273. };
  2274.  
  2275. // Fire it up!
  2276. inactivityTime();</pre></div>
  2277.  
  2278.  
  2279.  
  2280. <h3 class="wp-block-heading">Ready-made solutions that make life easier:</h3>
  2281.  
  2282.  
  2283.  
  2284. <h4 class="wp-block-heading"><a href="https://github.com/thorst/jquery-idletimer" target="_blank" rel="noopener">idle-timer.js</a> (if you&#8217;re using jQuery)</h4>
  2285.  
  2286.  
  2287.  
  2288. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">$(document).idleTimer({
  2289.  timeout: 30000, // 30 seconds
  2290.  idle: false
  2291. });
  2292.  
  2293. $(document).on("idle.idleTimer", function(event, elem, obj) {
  2294.  console.log("User wandered off");
  2295.  // Do your idle stuff here
  2296. });
  2297.  
  2298. $(document).on("active.idleTimer", function(event, elem, obj) {
  2299.  console.log("They're back!");
  2300.  // Welcome them back
  2301. });</pre></div>
  2302.  
  2303.  
  2304. <h4><a href="https://github.com/shawnmclean/Idle.js" target="_blank" rel="noopener">idle.js</a> (for vanilla JS lovers)</h4>
  2305.  
  2306.  
  2307. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">const idle = new IdleJs({
  2308.  idle: 30000, // 30 seconds
  2309.  onIdle: function() {
  2310.    console.log("Gone fishing...");
  2311.    // Handle idle state
  2312.  },
  2313.  onActive: function() {
  2314.    console.log("Back to work!");
  2315.    // Handle active state
  2316.  },
  2317.  events: ['mousemove', 'keydown', 'mousedown', 'touchstart', 'scroll']
  2318. });
  2319. idle.start();</pre></div>
  2320.  
  2321.  
  2322.  
  2323. <h3 class="wp-block-heading">Pro tips for idle detection:</h3>
  2324.  
  2325.  
  2326.  
  2327. <ol class="wp-block-list">
  2328. <li><strong>Don&#8217;t be too trigger-happy</strong> &#8211; Balance security with not annoying users</li>
  2329.  
  2330.  
  2331.  
  2332. <li><strong>Remember multiple tabs exist</strong> &#8211; They might be active elsewhere in your app</li>
  2333.  
  2334.  
  2335.  
  2336. <li><strong>Give fair warnings</strong> &#8211; Nobody likes being logged out without notice</li>
  2337.  
  2338.  
  2339.  
  2340. <li><strong>Be accessibility-friendly</strong> &#8211; Some folks need more time to interact</li>
  2341.  
  2342.  
  2343.  
  2344. <li><strong>Test on phones</strong> &#8211; Mobile interactions are a whole different ballgame</li>
  2345. </ol>
  2346.  
  2347.  
  2348.  
  2349. <h2 class="wp-block-heading">2. Online or Offline? &#8220;Is This Thing Connected?&#8221;</h2>
  2350.  
  2351.  
  2352.  
  2353. <h3 class="wp-block-heading">What&#8217;s the deal?</h3>
  2354.  
  2355.  
  2356.  
  2357. <p>This is about figuring out if your user has an internet connection. Super important if you want your app to work smoothly when someone&#8217;s connection drops (like in an elevator or on the subway).</p>
  2358.  
  2359.  
  2360.  
  2361. <h3 class="wp-block-heading">Why you&#8217;d want this:</h3>
  2362.  
  2363.  
  2364.  
  2365. <ul class="wp-block-list">
  2366. <li>Building apps that work offline</li>
  2367.  
  2368.  
  2369.  
  2370. <li>Saving data when someone goes offline, then syncing when they&#8217;re back</li>
  2371.  
  2372.  
  2373.  
  2374. <li>Showing handy &#8220;You&#8217;re offline!&#8221; messages</li>
  2375.  
  2376.  
  2377.  
  2378. <li>Disabling features that need internet when it&#8217;s not available</li>
  2379. </ul>
  2380.  
  2381.  
  2382.  
  2383. <h3 class="wp-block-heading"><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine" target="_blank" rel="noopener">The easiest way (Navigator.onLine):</a></h3>
  2384.  
  2385.  
  2386.  
  2387. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Check if we're online
  2388. const updateConnectionStatus = () =&gt; {
  2389.  const status = navigator.onLine ? "online" : "offline";
  2390.  console.log(`Looks like you're ${status}`);
  2391.  
  2392.  // Add a visual indicator
  2393.  document.body.classList.toggle('offline-mode', !navigator.onLine);
  2394. };
  2395.  
  2396. // Listen for changes
  2397. window.addEventListener('online', updateConnectionStatus);
  2398. window.addEventListener('offline', updateConnectionStatus);
  2399.  
  2400. // Check right away
  2401. updateConnectionStatus();</pre></div>
  2402.  
  2403.  
  2404.  
  2405. <h3 class="wp-block-heading">A more reliable approach (actually checking if we can reach the internet):</h3>
  2406.  
  2407.  
  2408.  
  2409. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">function checkRealConnectivity() {
  2410.  return fetch('https://www.example.com/ping', {
  2411.    method: 'HEAD',
  2412.    mode: 'no-cors',
  2413.    cache: 'no-store'
  2414.  })
  2415.  .then(() =&gt; {
  2416.    console.log("Yep, we're online!");
  2417.    document.body.classList.remove('offline-mode');
  2418.    return true;
  2419.  })
  2420.  .catch(() =&gt; {
  2421.    console.log("Nope, we're offline!");
  2422.    document.body.classList.add('offline-mode');
  2423.    return false;
  2424.  });
  2425. }
  2426.  
  2427. // Check when the page loads
  2428. checkRealConnectivity();
  2429.  
  2430. // Check every 30 seconds
  2431. setInterval(checkRealConnectivity, 30000);</pre></div>
  2432.  
  2433.  
  2434.  
  2435. <h3 class="wp-block-heading"><a href="https://github.com/sindresorhus/is-online" target="_blank" rel="noopener">The easy button (is-online library):</a></h3>
  2436.  
  2437.  
  2438.  
  2439. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">import isOnline from 'is-online';
  2440.  
  2441. async function checkConnection() {
  2442.  const online = await isOnline();
  2443.  console.log(`We're ${online ? 'connected! <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f389.png" alt="🎉" class="wp-smiley" style="height: 1em; max-height: 1em;" />' : 'offline <img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f622.png" alt="😢" class="wp-smiley" style="height: 1em; max-height: 1em;" />'}`);
  2444.  
  2445.  // Update the UI
  2446.  document.body.classList.toggle('offline-mode', !online);
  2447. }
  2448.  
  2449. // Check when the page loads
  2450. checkConnection();
  2451.  
  2452. // Check periodically
  2453. setInterval(checkConnection, 30000);</pre></div>
  2454.  
  2455.  
  2456.  
  2457. <h3 class="wp-block-heading">Pro tips for online/offline detection:</h3>
  2458.  
  2459.  
  2460.  
  2461. <ol class="wp-block-list">
  2462. <li><strong>Don&#8217;t trust Navigator.onLine alone</strong> &#8211; It can lie (especially on some browsers)</li>
  2463.  
  2464.  
  2465.  
  2466. <li><strong>Belt AND suspenders</strong> &#8211; Use both methods for best results</li>
  2467.  
  2468.  
  2469.  
  2470. <li><strong>Be ready for reconnection</strong> &#8211; Have a plan for when users come back online</li>
  2471.  
  2472.  
  2473.  
  2474. <li><strong>Make it obvious</strong> &#8211; Clear indicators when someone&#8217;s offline prevent frustration</li>
  2475.  
  2476.  
  2477.  
  2478. <li><strong>Design assuming offline</strong> &#8211; The web is flaky, plan accordingly!</li>
  2479. </ol>
  2480.  
  2481.  
  2482.  
  2483. <h2 class="wp-block-heading">3. Who&#8217;s Here Right Now? Real-time User Presence</h2>
  2484.  
  2485.  
  2486.  
  2487. <h3 class="wp-block-heading">What&#8217;s the deal?</h3>
  2488.  
  2489.  
  2490.  
  2491. <p>This is about knowing which users are currently active in multi-user applications. Think of the green dots next to names in chat apps or seeing who&#8217;s editing a Google Doc with you.</p>
  2492.  
  2493.  
  2494.  
  2495. <h3 class="wp-block-heading">Why you&#8217;d want this:</h3>
  2496.  
  2497.  
  2498.  
  2499. <ul class="wp-block-list">
  2500. <li>Chat apps showing who&#8217;s available</li>
  2501.  
  2502.  
  2503.  
  2504. <li>Collaborative editing tools</li>
  2505.  
  2506.  
  2507.  
  2508. <li>Multiplayer games</li>
  2509.  
  2510.  
  2511.  
  2512. <li>Those &#8220;Someone is typing&#8230;&#8221; indicators</li>
  2513. </ul>
  2514.  
  2515.  
  2516.  
  2517. <h3 class="wp-block-heading"><a href="https://firebase.google.com/docs/firestore/solutions/presence" target="_blank" rel="noopener">The popular option: Firebase</a></h3>
  2518.  
  2519.  
  2520.  
  2521. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Assuming Firebase is set up
  2522. const uid = firebase.auth().currentUser.uid;
  2523. const userStatusRef = firebase.database().ref('/status/' + uid);
  2524.  
  2525. // Define our states
  2526. const isOfflineForDatabase = {
  2527.  state: 'offline',
  2528.  last_changed: firebase.database.ServerValue.TIMESTAMP,
  2529. };
  2530.  
  2531. const isOnlineForDatabase = {
  2532.  state: 'online',
  2533.  last_changed: firebase.database.ServerValue.TIMESTAMP,
  2534. };
  2535.  
  2536. // The magic happens here
  2537. firebase.database().ref('.info/connected').on('value', (snapshot) =&gt; {
  2538.  if (snapshot.val() === false) {
  2539.    return;
  2540.  }
  2541.  
  2542.  // This is the clever bit - it runs on the server even if they
  2543.  // close the browser or lose connection
  2544.  userStatusRef.onDisconnect().set(isOfflineForDatabase).then(() =&gt; {
  2545.    // Now we can safely mark them as online
  2546.    userStatusRef.set(isOnlineForDatabase);
  2547.  });
  2548. });
  2549.  
  2550. // Get a list of who's currently online
  2551. firebase.database().ref('/status').on('value', (snapshot) =&gt; {
  2552.  const statuses = snapshot.val() || {};
  2553.  const activeUsers = Object.keys(statuses).filter(key =&gt;
  2554.    statuses[key].state === 'online'
  2555.  );
  2556.  console.log(`${activeUsers.length} people hanging out here`);
  2557.  // Update your UI with who's around
  2558. });</pre></div>
  2559.  
  2560.  
  2561.  
  2562. <h3 class="wp-block-heading"><a href="https://socket.io/" target="_blank" rel="noopener">The DIY approach: Socket.io</a></h3>
  2563.  
  2564.  
  2565.  
  2566. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Server-side code (Node.js)
  2567. const io = require('socket.io')(server);
  2568. const users = {};
  2569.  
  2570. io.on('connection', (socket) =&gt; {
  2571.  // Someone shows up
  2572.  socket.on('user-connect', (userId) =&gt; {
  2573.    users[userId] = {
  2574.      id: userId,
  2575.      socketId: socket.id,
  2576.      lastActive: new Date()
  2577.    };
  2578.    io.emit('user-presence-update', users);
  2579.  });
  2580.  
  2581.  // They did something
  2582.  socket.on('user-activity', (userId) =&gt; {
  2583.    if (users[userId]) {
  2584.      users[userId].lastActive = new Date();
  2585.    }
  2586.  });
  2587.  
  2588.  // They left
  2589.  socket.on('disconnect', () =&gt; {
  2590.    const userId = Object.keys(users).find(id =&gt; users[id].socketId === socket.id);
  2591.    if (userId) {
  2592.      delete users[userId];
  2593.      io.emit('user-presence-update', users);
  2594.    }
  2595.  });
  2596. });
  2597.  
  2598. // Client-side code
  2599. const socket = io();
  2600. const userId = 'user123'; // Usually from login
  2601.  
  2602. // Let everyone know we're here
  2603. socket.emit('user-connect', userId);
  2604.  
  2605. // Ping periodically to show we're still active
  2606. setInterval(() =&gt; {
  2607.  socket.emit('user-activity', userId);
  2608. }, 30000);
  2609.  
  2610. // Listen for updates about who's around
  2611. socket.on('user-presence-update', (users) =&gt; {
  2612.  console.log(`There are ${Object.keys(users).length} people here`);
  2613.  // Update your UI
  2614. });</pre></div>
  2615.  
  2616.  
  2617.  
  2618. <h3 class="wp-block-heading">Pro tips for real-time presence:</h3>
  2619.  
  2620.  
  2621.  
  2622. <ol class="wp-block-list">
  2623. <li><strong>Be forgiving about disconnections</strong> &#8211; Network hiccups shouldn&#8217;t mark people offline immediately</li>
  2624.  
  2625.  
  2626.  
  2627. <li><strong>Respect privacy</strong> &#8211; Let users go &#8220;invisible&#8221; if they want</li>
  2628.  
  2629.  
  2630.  
  2631. <li><strong>Add timeouts</strong> &#8211; Show &#8220;away&#8221; status before &#8220;offline&#8221;</li>
  2632.  
  2633.  
  2634.  
  2635. <li><strong>Think about scale</strong> &#8211; Presence updates can create lots of traffic</li>
  2636.  
  2637.  
  2638.  
  2639. <li><strong>Use clear indicators</strong> &#8211; Make status easy to understand at a glance</li>
  2640. </ol>
  2641.  
  2642.  
  2643.  
  2644. <h2 class="wp-block-heading">4. Are They Actually Looking At Us? Tab/Visibility Detection</h2>
  2645.  
  2646.  
  2647.  
  2648. <h3 class="wp-block-heading">What&#8217;s the deal?</h3>
  2649.  
  2650.  
  2651.  
  2652. <p>This is about knowing if your web page is currently visible to the user or if it&#8217;s hidden in a background tab or minimized window. Great for conserving resources and improving performance.</p>
  2653.  
  2654.  
  2655.  
  2656. <h3 class="wp-block-heading">Why you&#8217;d want this:</h3>
  2657.  
  2658.  
  2659.  
  2660. <ul class="wp-block-list">
  2661. <li>Pausing videos when people switch tabs</li>
  2662.  
  2663.  
  2664.  
  2665. <li>Saving battery by stopping animations</li>
  2666.  
  2667.  
  2668.  
  2669. <li>Changing notification sounds/visuals</li>
  2670.  
  2671.  
  2672.  
  2673. <li>Knowing actual viewing time for analytics</li>
  2674. </ul>
  2675.  
  2676.  
  2677.  
  2678. <h3 class="wp-block-heading"><a href="https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API" target="_blank" rel="noopener">The modern way: Page Visibility API</a></h3>
  2679.  
  2680.  
  2681.  
  2682. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Handle visibility changes
  2683. function handleVisibilityChange() {
  2684.  if (document.hidden) {
  2685.    console.log("Tab is hidden - taking a break!");
  2686.    // Pause stuff
  2687.    document.querySelector('video')?.pause();
  2688.  } else {
  2689.    console.log("Tab is visible - back to work!");
  2690.    // Resume stuff
  2691.    document.querySelector('video')?.play();
  2692.  }
  2693. }
  2694.  
  2695. // Listen for changes
  2696. document.addEventListener('visibilitychange', handleVisibilityChange);
  2697.  
  2698. // Check right away
  2699. handleVisibilityChange();</pre></div>
  2700.  
  2701.  
  2702.  
  2703. <h3 class="wp-block-heading">The old-school approach: Focus/Blur</h3>
  2704.  
  2705.  
  2706.  
  2707. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">window.addEventListener('focus', () =&gt; {
  2708.  console.log("Window active - hello again!");
  2709.  // Resume heavy stuff
  2710.  document.querySelector('video')?.play();
  2711. });
  2712.  
  2713. window.addEventListener('blur', () =&gt; {
  2714.  console.log("Window inactive - taking a breather");
  2715.  // Pause heavy stuff
  2716.  document.querySelector('video')?.pause();
  2717. });
  2718.  
  2719. // Check right away
  2720. console.log(`Window is ${document.hasFocus() ? 'focused' : 'not focused'} right now`);</pre></div>
  2721.  
  2722.  
  2723.  
  2724. <h3 class="wp-block-heading"><a href="https://github.com/ai/visibilityjs" target="_blank" rel="noopener">The helper library: visibilityjs</a></h3>
  2725.  
  2726.  
  2727.  
  2728. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Is the page hidden?
  2729. if (Visibility.hidden()) {
  2730.  console.log('Page is in the background');
  2731. }
  2732.  
  2733. // Run when visibility changes
  2734. Visibility.change((e, state) =&gt; {
  2735.  console.log(`Visibility changed to: ${state}`);
  2736. });
  2737.  
  2738. // Run when page becomes visible
  2739. Visibility.onVisible(() =&gt; {
  2740.  console.log('Welcome back!');
  2741. });
  2742.  
  2743. // Get current state
  2744. console.log(`Current state: ${Visibility.state()}`);</pre></div>
  2745.  
  2746.  
  2747.  
  2748. <h3 class="wp-block-heading">Pro tips for visibility detection:</h3>
  2749.  
  2750.  
  2751.  
  2752. <ol class="wp-block-list">
  2753. <li><strong>Page Visibility API > focus/blur</strong> &#8211; It&#8217;s more reliable</li>
  2754.  
  2755.  
  2756.  
  2757. <li><strong>Think about mobile</strong> &#8211; Mobile browsers handle visibility differently</li>
  2758.  
  2759.  
  2760.  
  2761. <li><strong>Test on different browsers</strong> &#8211; Implementation varies</li>
  2762.  
  2763.  
  2764.  
  2765. <li><strong>Hidden doesn&#8217;t mean inactive</strong> &#8211; They might be listening to your audio</li>
  2766.  
  2767.  
  2768.  
  2769. <li><strong>Combine with idle detection</strong> &#8211; For the full picture of user engagement</li>
  2770. </ol>
  2771.  
  2772.  
  2773.  
  2774. <h2 class="wp-block-heading">What&#8217;s the Best Option? A Quick Comparison</h2>
  2775.  
  2776.  
  2777.  
  2778. <p>Here&#8217;s a no-nonsense ranking of the most popular solutions:</p>
  2779.  
  2780.  
  2781.  
  2782. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Solution" class="mtr-th-tag"><div class="mtr-cell-content">Solution</div></th><th data-mtr-content="Popularity" class="mtr-th-tag"><div class="mtr-cell-content">Popularity</div></th><th data-mtr-content="Reliability" class="mtr-th-tag"><div class="mtr-cell-content">Reliability</div></th><th data-mtr-content="Difficulty" class="mtr-th-tag"><div class="mtr-cell-content">Difficulty</div></th><th data-mtr-content="Best For" class="mtr-th-tag"><div class="mtr-cell-content">Best For</div></th></tr></thead><tbody><tr><td data-mtr-content="Solution" class="mtr-td-tag"><div class="mtr-cell-content"><strong><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine" target="_blank" rel="noopener">Navigator.onLine</a></strong></div></td><td data-mtr-content="Popularity" class="mtr-td-tag"><div class="mtr-cell-content">Extremely High</div></td><td data-mtr-content="Reliability" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="Difficulty" class="mtr-td-tag"><div class="mtr-cell-content">Very Easy</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Quick online/offline checks</div></td></tr><tr><td data-mtr-content="Solution" class="mtr-td-tag"><div class="mtr-cell-content"><strong>Vanilla JS Idle Detection</strong></div></td><td data-mtr-content="Popularity" class="mtr-td-tag"><div class="mtr-cell-content">Very High</div></td><td data-mtr-content="Reliability" class="mtr-td-tag"><div class="mtr-cell-content">High</div></td><td data-mtr-content="Difficulty" class="mtr-td-tag"><div class="mtr-cell-content">Easy</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Auto-logout and activity tracking</div></td></tr><tr><td data-mtr-content="Solution" class="mtr-td-tag"><div class="mtr-cell-content"><strong><a href="https://firebase.google.com/docs/firestore/solutions/presence" target="_blank" rel="noopener">Firebase Presence</a></strong></div></td><td data-mtr-content="Popularity" class="mtr-td-tag"><div class="mtr-cell-content">High</div></td><td data-mtr-content="Reliability" class="mtr-td-tag"><div class="mtr-cell-content">Very High</div></td><td data-mtr-content="Difficulty" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Chat apps and collaboration tools</div></td></tr><tr><td data-mtr-content="Solution" class="mtr-td-tag"><div class="mtr-cell-content"><strong><a href="https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API" target="_blank" rel="noopener">Page Visibility API</a></strong></div></td><td data-mtr-content="Popularity" class="mtr-td-tag"><div class="mtr-cell-content">High</div></td><td data-mtr-content="Reliability" class="mtr-td-tag"><div class="mtr-cell-content">High</div></td><td data-mtr-content="Difficulty" class="mtr-td-tag"><div class="mtr-cell-content">Easy</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Pausing videos and animations</div></td></tr><tr><td data-mtr-content="Solution" class="mtr-td-tag"><div class="mtr-cell-content"><strong><a href="https://socket.io/" target="_blank" rel="noopener">Socket.io Presence</a></strong></div></td><td data-mtr-content="Popularity" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="Reliability" class="mtr-td-tag"><div class="mtr-cell-content">High</div></td><td data-mtr-content="Difficulty" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Custom real-time presence systems</div></td></tr></tbody></table></figure>
  2783.  
  2784.  
  2785.  
  2786. <h2 class="wp-block-heading">How to Choose What&#8217;s Right for Your Project</h2>
  2787.  
  2788.  
  2789.  
  2790. <p>When picking a solution, ask yourself:</p>
  2791.  
  2792.  
  2793.  
  2794. <ol class="wp-block-list">
  2795. <li><strong>What exactly am I trying to detect?</strong> (Idle users? Connection status? Who&#8217;s online?)</li>
  2796.  
  2797.  
  2798.  
  2799. <li><strong>How many users will I have?</strong> (Some solutions don&#8217;t scale well)</li>
  2800.  
  2801.  
  2802.  
  2803. <li><strong>Do I need something simple or feature-rich?</strong> (Balance complexity with needs)</li>
  2804.  
  2805.  
  2806.  
  2807. <li><strong>Am I okay using third-party services?</strong> (Or need to keep everything in-house)</li>
  2808.  
  2809.  
  2810.  
  2811. <li><strong>Which browsers need to be supported?</strong> (Older browsers need different approaches)</li>
  2812. </ol>
  2813.  
  2814.  
  2815.  
  2816. <h2 class="wp-block-heading">Wrapping Up</h2>
  2817.  
  2818.  
  2819.  
  2820. <p>Presence detection might seem like a small detail, but it can make a huge difference in how your app feels. Whether you&#8217;re building the next big social platform or just trying to make your web app more responsive, these techniques can help you create a better experience for your users.</p>
  2821.  
  2822.  
  2823.  
  2824. <p>Remember that you can (and often should) combine different approaches &#8211; for example, using both idle detection and visibility detection together will give you a much clearer picture of what your users are actually doing.</p>
  2825.  
  2826.  
  2827.  
  2828. <p>And always, always test on different devices and browsers! Nothing&#8217;s worse than deploying something that works great on your machine but falls apart on your users&#8217; devices.</p>
  2829.  
  2830.  
  2831.  
  2832. <p>Want to dig deeper? Check out these resources:</p>
  2833.  
  2834.  
  2835.  
  2836. <ul class="wp-block-list">
  2837. <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API" target="_blank" rel="noopener">MDN&#8217;s Page Visibility API docs</a></li>
  2838.  
  2839.  
  2840.  
  2841. <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine" target="_blank" rel="noopener">All about Navigator.onLine</a></li>
  2842.  
  2843.  
  2844.  
  2845. <li><a href="https://firebase.google.com/docs/database/web/offline-capabilities" target="_blank" rel="noopener">Firebase&#8217;s offline capabilities</a></li>
  2846.  
  2847.  
  2848.  
  2849. <li><a href="https://socket.io/docs/v4/" target="_blank" rel="noopener">Socket.io docs</a></li>
  2850. </ul>
  2851.  
  2852.  
  2853.  
  2854.  
  2855.  
  2856.  
  2857.  
  2858.  
  2859. ]]></content:encoded>
  2860. <wfw:commentRss>https://portalzine.de/the-ultimate-guide-to-presence-detection-in-javascript-what-works-and-why/feed/</wfw:commentRss>
  2861. <slash:comments>0</slash:comments>
  2862. </item>
  2863. <item>
  2864. <title>Best Server Hosts in Europe 2025: The Ultimate Guide</title>
  2865. <link>https://portalzine.de/best-server-hosts-in-europe-2025-the-ultimate-guide/</link>
  2866. <comments>https://portalzine.de/best-server-hosts-in-europe-2025-the-ultimate-guide/#respond</comments>
  2867. <dc:creator><![CDATA[]]></dc:creator>
  2868. <pubDate>Sat, 12 Apr 2025 09:02:02 +0000</pubDate>
  2869. <category><![CDATA[Development]]></category>
  2870. <category><![CDATA[Deployment]]></category>
  2871. <category><![CDATA[Hosting]]></category>
  2872. <guid isPermaLink="false">https://portalzine.de/?p=1733</guid>
  2873.  
  2874. <description><![CDATA[Looking for a rock-solid European server host in 2025? You&#8217;re not alone! With increasing concerns about data privacy, GDPR compliance, and the desire to keep data within European borders, more businesses than ever are seeking out EU-based hosting solutions. In this guide, I&#8217;ll walk you through the absolute best server hosts in Europe right now, [&#8230;]]]></description>
  2875. <content:encoded><![CDATA[
  2876. <p>Looking for a rock-solid European server host in 2025? You&#8217;re not alone! With increasing concerns about data privacy, GDPR compliance, and the desire to keep data within European borders, more businesses than ever are seeking out EU-based hosting solutions.</p>
  2877.  
  2878.  
  2879.  
  2880. <p>In this guide, I&#8217;ll walk you through the absolute best server hosts in Europe right now, with all the juicy details on performance, pricing, and special features that make each one stand out. Whether you&#8217;re running a small blog or managing enterprise-level infrastructure, I&#8217;ve got you covered!</p>
  2881.  
  2882.  
  2883.  
  2884. <h2 class="wp-block-heading">Why Choose a European Server Host in 2025?</h2>
  2885.  
  2886.  
  2887.  
  2888. <p>Before diving into the rankings, let&#8217;s talk about why European hosting is having such a moment in 2025:</p>
  2889.  
  2890.  
  2891.  
  2892. <ul class="wp-block-list">
  2893. <li><strong>GDPR Compliance</strong>: European providers naturally align with the EU&#8217;s strict data protection regulations</li>
  2894.  
  2895.  
  2896.  
  2897. <li><strong>Data Sovereignty</strong>: Keep your data within European borders and away from foreign jurisdiction</li>
  2898.  
  2899.  
  2900.  
  2901. <li><strong>Reduced Latency</strong>: Faster speeds for European users (a huge plus for user experience!)</li>
  2902.  
  2903.  
  2904.  
  2905. <li><strong>Transparent Pricing</strong>: Many European hosts offer simpler pricing models without the surprise fees</li>
  2906.  
  2907.  
  2908.  
  2909. <li><strong>Sustainability Focus</strong>: European data centers often lead the way in green energy initiatives</li>
  2910.  
  2911.  
  2912.  
  2913. <li><strong>Political Stability</strong>: Recent changes in US politics have created uncertainty around transatlantic data transfers</li>
  2914. </ul>
  2915.  
  2916.  
  2917.  
  2918. <h2 class="wp-block-heading">The Top Server Hosts in Europe for 2025</h2>
  2919.  
  2920.  
  2921.  
  2922. <h3 class="wp-block-heading">1. Hetzner (Germany) &#8211; Best Overall Value</h3>
  2923.  
  2924.  
  2925.  
  2926. <p><strong>Why they&#8217;re awesome</strong>: Hetzner has earned the top spot by delivering an almost unbeatable combination of performance and affordability.</p>
  2927.  
  2928.  
  2929.  
  2930. <p><strong>Standout features</strong>:</p>
  2931.  
  2932.  
  2933.  
  2934. <ul class="wp-block-list">
  2935. <li>High-performance infrastructure with AMD EPYC CPUs and NVMe SSDs</li>
  2936.  
  2937.  
  2938.  
  2939. <li>Ridiculously competitive pricing (starting at just €4.15/month for cloud instances)</li>
  2940.  
  2941.  
  2942.  
  2943. <li>100% green energy powered data centers in Germany and Finland</li>
  2944.  
  2945.  
  2946.  
  2947. <li>Strict GDPR compliance with all data stored within the EU</li>
  2948.  
  2949.  
  2950.  
  2951. <li>Rock-solid reliability with 24/7 uptime monitoring</li>
  2952. </ul>
  2953.  
  2954.  
  2955.  
  2956. <p><strong>Perfect for</strong>: Businesses looking for the best performance-to-price ratio, developers, and budget-conscious projects that don&#8217;t want to sacrifice quality.</p>
  2957.  
  2958.  
  2959.  
  2960. <p><strong>Starting price</strong>: Cloud instances from €4.15/month, dedicated servers from €34/month</p>
  2961.  
  2962.  
  2963.  
  2964. <p><strong>Visit</strong>: <a href="https://www.hetzner.com/" target="_blank" rel="noopener">Hetzner</a></p>
  2965.  
  2966.  
  2967.  
  2968. <h3 class="wp-block-heading">2. OVHcloud (France) &#8211; Best for Enterprise Solutions</h3>
  2969.  
  2970.  
  2971.  
  2972. <p><strong>Why they&#8217;re awesome</strong>: As one of Europe&#8217;s largest cloud providers, OVHcloud offers an impressive range of hosting options with a strong focus on security and compliance.</p>
  2973.  
  2974.  
  2975.  
  2976. <p><strong>Standout features</strong>:</p>
  2977.  
  2978.  
  2979.  
  2980. <ul class="wp-block-list">
  2981. <li>Extensive service portfolio (VPS, dedicated servers, private cloud)</li>
  2982.  
  2983.  
  2984.  
  2985. <li>Data centers across multiple European locations for optimal performance</li>
  2986.  
  2987.  
  2988.  
  2989. <li>Strong emphasis on data sovereignty under GDPR</li>
  2990.  
  2991.  
  2992.  
  2993. <li>Industry-leading sustainability initiatives</li>
  2994.  
  2995.  
  2996.  
  2997. <li>Excellent DDoS protection included</li>
  2998.  
  2999.  
  3000.  
  3001. <li>4-star rating on Trustpilot in 2025</li>
  3002. </ul>
  3003.  
  3004.  
  3005.  
  3006. <p><strong>Perfect for</strong>: Large enterprises, businesses with complex hosting needs, and companies requiring the highest levels of security and compliance.</p>
  3007.  
  3008.  
  3009.  
  3010. <p><strong>Starting price</strong>: Plans from €4.00/month</p>
  3011.  
  3012.  
  3013.  
  3014. <p><strong>Visit</strong>: <a href="https://www.ovhcloud.com/" target="_blank" rel="noopener">OVHcloud</a></p>
  3015.  
  3016.  
  3017.  
  3018. <h3 class="wp-block-heading">3. IONOS (Germany) &#8211; Best for Small to Medium Businesses</h3>
  3019.  
  3020.  
  3021.  
  3022. <p><strong>Why they&#8217;re awesome</strong>: IONOS delivers enterprise-grade features with user-friendly interfaces, making it perfect for businesses without dedicated IT teams.</p>
  3023.  
  3024.  
  3025.  
  3026. <p><strong>Standout features</strong>:</p>
  3027.  
  3028.  
  3029.  
  3030. <ul class="wp-block-list">
  3031. <li>Highly scalable cloud solutions for growing businesses</li>
  3032.  
  3033.  
  3034.  
  3035. <li>Excellent security features with data encryption and automated backups</li>
  3036.  
  3037.  
  3038.  
  3039. <li>Intuitive control panel that makes management easy</li>
  3040.  
  3041.  
  3042.  
  3043. <li>Strong focus on customer support</li>
  3044.  
  3045.  
  3046.  
  3047. <li>Competitive pricing model with no hidden fees</li>
  3048.  
  3049.  
  3050.  
  3051. <li>European data centers ensuring GDPR compliance</li>
  3052. </ul>
  3053.  
  3054.  
  3055.  
  3056. <p><strong>Perfect for</strong>: Small to medium businesses, startups scaling their operations, and companies that value ease of use.</p>
  3057.  
  3058.  
  3059.  
  3060. <p><strong>Starting price</strong>: Starting at €1.00/month</p>
  3061.  
  3062.  
  3063.  
  3064. <p><strong>Visit</strong>: <a href="https://www.ionos.com/" target="_blank" rel="noopener">IONOS</a></p>
  3065.  
  3066.  
  3067.  
  3068. <h3 class="wp-block-heading">4. Scaleway (France) &#8211; Best for Developers</h3>
  3069.  
  3070.  
  3071.  
  3072. <p><strong>Why they&#8217;re awesome</strong>: Scaleway has positioned itself as the developer-friendly cloud provider with flexible solutions and excellent APIs.</p>
  3073.  
  3074.  
  3075.  
  3076. <p><strong>Standout features</strong>:</p>
  3077.  
  3078.  
  3079.  
  3080. <ul class="wp-block-list">
  3081. <li>Developer-focused tools and APIs</li>
  3082.  
  3083.  
  3084.  
  3085. <li>Kubernetes clusters and containerization support</li>
  3086.  
  3087.  
  3088.  
  3089. <li>Eco-friendly data centers powered by renewable energy</li>
  3090.  
  3091.  
  3092.  
  3093. <li>GDPR-compliant with data centers in France</li>
  3094.  
  3095.  
  3096.  
  3097. <li>Simple, predictable pricing model</li>
  3098. </ul>
  3099.  
  3100.  
  3101.  
  3102. <p><strong>Perfect for</strong>: Developers, tech startups, and companies using container-based deployments.</p>
  3103.  
  3104.  
  3105.  
  3106. <p><strong>Starting price</strong>: Plans from €2.99/month</p>
  3107.  
  3108.  
  3109.  
  3110. <p><strong>Visit</strong>: <a href="https://www.scaleway.com/" target="_blank" rel="noopener">Scaleway</a></p>
  3111.  
  3112.  
  3113.  
  3114. <h3 class="wp-block-heading">5. Contabo (Germany) &#8211; Best Budget Option</h3>
  3115.  
  3116.  
  3117.  
  3118. <p><strong>Why they&#8217;re awesome</strong>: Contabo offers some of the most affordable server hosting in Europe without significantly compromising on quality.</p>
  3119.  
  3120.  
  3121.  
  3122. <p><strong>Standout features</strong>:</p>
  3123.  
  3124.  
  3125.  
  3126. <ul class="wp-block-list">
  3127. <li>Incredibly low-priced VPS hosting with generous resources</li>
  3128.  
  3129.  
  3130.  
  3131. <li>Data centers in Germany and other EU countries ensuring compliance</li>
  3132.  
  3133.  
  3134.  
  3135. <li>Free DDoS protection on all plans</li>
  3136.  
  3137.  
  3138.  
  3139. <li>Highly scalable VPS options with up to 60GB RAM and 10 CPUs</li>
  3140. </ul>
  3141.  
  3142.  
  3143.  
  3144. <p><strong>Perfect for</strong>: Budget-conscious projects, startups, small businesses, and personal websites.</p>
  3145.  
  3146.  
  3147.  
  3148. <p><strong>Starting price</strong>: VPS hosting from €3.99/month, dedicated servers from €49.99/month</p>
  3149.  
  3150.  
  3151.  
  3152. <p><strong>Visit</strong>: <a href="https://contabo.com/" target="_blank" rel="noopener">Contabo</a></p>
  3153.  
  3154.  
  3155.  
  3156. <h3 class="wp-block-heading">6. UpCloud (Finland) &#8211; Best for Performance</h3>
  3157.  
  3158.  
  3159.  
  3160. <p><strong>Why they&#8217;s awesome</strong>: This Finnish provider has made a name for itself with blazing-fast storage capabilities and responsive infrastructure.</p>
  3161.  
  3162.  
  3163.  
  3164. <p><strong>Standout features</strong>:</p>
  3165.  
  3166.  
  3167.  
  3168. <ul class="wp-block-list">
  3169. <li>MaxIOPS storage technology for exceptional performance</li>
  3170.  
  3171.  
  3172.  
  3173. <li>100% uptime SLA guarantee</li>
  3174.  
  3175.  
  3176.  
  3177. <li>Excellent server response times</li>
  3178.  
  3179.  
  3180.  
  3181. <li>Strong security features</li>
  3182.  
  3183.  
  3184.  
  3185. <li>Data centers in multiple European locations</li>
  3186. </ul>
  3187.  
  3188.  
  3189.  
  3190. <p><strong>Perfect for</strong>: Performance-critical applications, businesses where speed is crucial, and high-traffic websites.</p>
  3191.  
  3192.  
  3193.  
  3194. <p><strong>Starting price</strong>: Plans from €3.00/month</p>
  3195.  
  3196.  
  3197.  
  3198. <p><strong>Visit</strong>: <a href="https://upcloud.com/" target="_blank" rel="noopener">UpCloud</a></p>
  3199.  
  3200.  
  3201.  
  3202. <h3 class="wp-block-heading">7. Exoscale (Switzerland) &#8211; Best for Privacy &amp; Compliance</h3>
  3203.  
  3204.  
  3205.  
  3206. <p><strong>Why they&#8217;re awesome</strong>: Switzerland&#8217;s strict data protection laws make Exoscale an excellent choice for companies with stringent privacy requirements.</p>
  3207.  
  3208.  
  3209.  
  3210. <p><strong>Standout features</strong>:</p>
  3211.  
  3212.  
  3213.  
  3214. <ul class="wp-block-list">
  3215. <li>Swiss data privacy laws (some of the strongest in the world)</li>
  3216.  
  3217.  
  3218.  
  3219. <li>Full GDPR compliance</li>
  3220.  
  3221.  
  3222.  
  3223. <li>Multiple data centers across Switzerland, Germany, and Austria</li>
  3224.  
  3225.  
  3226.  
  3227. <li>Scalable Kubernetes and compute instances</li>
  3228.  
  3229.  
  3230.  
  3231. <li>Specialized cloud solutions for regulated industries</li>
  3232. </ul>
  3233.  
  3234.  
  3235.  
  3236. <p><strong>Perfect for</strong>: Financial services, healthcare, government sectors, and any business prioritizing privacy.</p>
  3237.  
  3238.  
  3239.  
  3240. <p><strong>Starting price</strong>: Cloud instances from €5.50/month</p>
  3241.  
  3242.  
  3243.  
  3244. <p><strong>Visit</strong>: <a href="https://www.exoscale.com/" target="_blank" rel="noopener">Exoscale</a></p>
  3245.  
  3246.  
  3247.  
  3248. <h3 class="wp-block-heading">8. CloudSigma (Switzerland) &#8211; Most Customizable</h3>
  3249.  
  3250.  
  3251.  
  3252. <p><strong>Why they&#8217;re awesome</strong>: CloudSigma offers highly flexible hosting options where you can tailor every aspect of your environment.</p>
  3253.  
  3254.  
  3255.  
  3256. <p><strong>Standout features</strong>:</p>
  3257.  
  3258.  
  3259.  
  3260. <ul class="wp-block-list">
  3261. <li>Fully customizable resource allocation</li>
  3262.  
  3263.  
  3264.  
  3265. <li>Private networking and hybrid cloud capabilities</li>
  3266.  
  3267.  
  3268.  
  3269. <li>Swiss-based GDPR-compliant cloud computing</li>
  3270.  
  3271.  
  3272.  
  3273. <li>Flexible pay-as-you-go pricing model</li>
  3274.  
  3275.  
  3276.  
  3277. <li>ISO 27001 certified security</li>
  3278. </ul>
  3279.  
  3280.  
  3281.  
  3282. <p><strong>Perfect for</strong>: Businesses with unique infrastructure requirements and those seeking custom cloud environments.</p>
  3283.  
  3284.  
  3285.  
  3286. <p><strong>Starting price</strong>: Cloud instances from €7.00/month with consumption-based billing</p>
  3287.  
  3288.  
  3289.  
  3290. <p><strong>Visit</strong>: <a href="https://www.cloudsigma.com/" target="_blank" rel="noopener">CloudSigma</a></p>
  3291.  
  3292.  
  3293.  
  3294. <h3 class="wp-block-heading">9. Leaseweb (Netherlands) &#8211; Best Global Reach</h3>
  3295.  
  3296.  
  3297.  
  3298. <p><strong>Why they&#8217;re awesome</strong>: While based in Europe, Leaseweb offers exceptional global connectivity for companies serving international markets.</p>
  3299.  
  3300.  
  3301.  
  3302. <p><strong>Standout features</strong>:</p>
  3303.  
  3304.  
  3305.  
  3306. <ul class="wp-block-list">
  3307. <li>Enterprise-grade infrastructure with 99.999% uptime SLA</li>
  3308.  
  3309.  
  3310.  
  3311. <li>Hybrid and customizable cloud solutions</li>
  3312.  
  3313.  
  3314.  
  3315. <li>Advanced security including built-in DDoS protection</li>
  3316.  
  3317.  
  3318.  
  3319. <li>Data centers across Europe and globally for maximum reach</li>
  3320.  
  3321.  
  3322.  
  3323. <li>Excellent support for high-traffic applications</li>
  3324. </ul>
  3325.  
  3326.  
  3327.  
  3328. <p><strong>Perfect for</strong>: Enterprise applications, SaaS companies, and high-traffic websites with global audiences.</p>
  3329.  
  3330.  
  3331.  
  3332. <p><strong>Starting price</strong>: Cloud hosting from €10.00/month</p>
  3333.  
  3334.  
  3335.  
  3336. <p><strong>Visit</strong>: <a href="https://www.leaseweb.com/" target="_blank" rel="noopener">Leaseweb</a></p>
  3337.  
  3338.  
  3339.  
  3340. <h3 class="wp-block-heading">10. Hostinger (Lithuania) &#8211; Best User Experience</h3>
  3341.  
  3342.  
  3343.  
  3344. <p><strong>Why they&#8217;re awesome</strong>: Hostinger combines ease of use with affordable pricing and good performance.</p>
  3345.  
  3346.  
  3347.  
  3348. <p><strong>Standout features</strong>:</p>
  3349.  
  3350.  
  3351.  
  3352. <ul class="wp-block-list">
  3353. <li>Servers in multiple European locations for optimal performance</li>
  3354.  
  3355.  
  3356.  
  3357. <li>User-friendly drag-and-drop website builder</li>
  3358.  
  3359.  
  3360.  
  3361. <li>Economical plans with numerous free tools</li>
  3362.  
  3363.  
  3364.  
  3365. <li>Good balance of features for beginners and advanced users</li>
  3366. </ul>
  3367.  
  3368.  
  3369.  
  3370. <p><strong>Perfect for</strong>: Beginners, small businesses, and those prioritizing ease of use.</p>
  3371.  
  3372.  
  3373.  
  3374. <p><strong>Starting price</strong>: Plans from €2.78/month</p>
  3375.  
  3376.  
  3377.  
  3378. <p><strong>Visit</strong>: <a href="https://www.hostinger.com/" target="_blank" rel="noopener">Hostinger</a></p>
  3379.  
  3380.  
  3381.  
  3382. <h2 class="wp-block-heading">Comparison Table: European Server Hosts in 2025</h2>
  3383.  
  3384.  
  3385.  
  3386. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Provider" class="mtr-th-tag"><div class="mtr-cell-content">Provider</div></th><th data-mtr-content="Starting Price" class="mtr-th-tag"><div class="mtr-cell-content">Starting Price</div></th><th data-mtr-content="Best For" class="mtr-th-tag"><div class="mtr-cell-content">Best For</div></th><th data-mtr-content="Data Center Locations" class="mtr-th-tag"><div class="mtr-cell-content">Data Center Locations</div></th><th data-mtr-content="Green Energy" class="mtr-th-tag"><div class="mtr-cell-content">Green Energy</div></th><th data-mtr-content="GDPR Compliant" class="mtr-th-tag"><div class="mtr-cell-content">GDPR Compliant</div></th></tr></thead><tbody><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Hetzner</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€4.15/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Overall Value</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Germany, Finland</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">OVHcloud</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€4.00/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Enterprise Solutions</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">France, Germany, UK, Poland</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">IONOS</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€1.00/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">SMBs</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Germany, UK, Spain</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Scaleway</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€2.99/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Developers</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">France, Netherlands, Poland</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Contabo</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€3.99/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Budget Option</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Germany, EU Countries</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">UpCloud</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€3.00/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Performance</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Finland, UK, Germany</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Exoscale</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€5.50/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Privacy &amp; Compliance</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Switzerland, Austria, Germany</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">CloudSigma</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€7.00/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Customization</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Switzerland, Germany</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Leaseweb</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€10.00/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">Global Reach</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Netherlands, Germany, UK</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr><tr><td data-mtr-content="Provider" class="mtr-td-tag"><div class="mtr-cell-content">Hostinger</div></td><td data-mtr-content="Starting Price" class="mtr-td-tag"><div class="mtr-cell-content">€2.78/month</div></td><td data-mtr-content="Best For" class="mtr-td-tag"><div class="mtr-cell-content">User Experience</div></td><td data-mtr-content="Data Center Locations" class="mtr-td-tag"><div class="mtr-cell-content">Lithuania, Netherlands, UK</div></td><td data-mtr-content="Green Energy" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td><td data-mtr-content="GDPR Compliant" class="mtr-td-tag"><div class="mtr-cell-content">Yes</div></td></tr></tbody></table></figure>
  3387.  
  3388.  
  3389.  
  3390. <h2 class="wp-block-heading">How to Choose the Right European Server Host in 2025</h2>
  3391.  
  3392.  
  3393.  
  3394. <p>Still not sure which provider is right for you? Here are some quick guidelines:</p>
  3395.  
  3396.  
  3397.  
  3398. <ul class="wp-block-list">
  3399. <li><strong>If price is your main concern</strong>: Go with Contabo or Hetzner</li>
  3400.  
  3401.  
  3402.  
  3403. <li><strong>If you need top-tier performance</strong>: UpCloud or OVHcloud</li>
  3404.  
  3405.  
  3406.  
  3407. <li><strong>If privacy is paramount</strong>: Exoscale or CloudSigma (Swiss-based)</li>
  3408.  
  3409.  
  3410.  
  3411. <li><strong>If you&#8217;re just starting out</strong>: Hostinger or IONOS</li>
  3412.  
  3413.  
  3414.  
  3415. <li><strong>If you&#8217;re a developer</strong>: Scaleway or Hetzner</li>
  3416.  
  3417.  
  3418.  
  3419. <li><strong>If you have complex enterprise needs</strong>: OVHcloud or Leaseweb</li>
  3420. </ul>
  3421.  
  3422.  
  3423.  
  3424. <h2 class="wp-block-heading">The Future of European Hosting</h2>
  3425.  
  3426.  
  3427.  
  3428. <p>European cloud providers are gaining serious momentum as viable alternatives to AWS, Google Cloud, and Azure. With the EU strengthening its digital sovereignty initiatives like GAIA-X, and increasing concerns about data transfers to the US, the trend toward European hosting solutions is likely to continue growing throughout 2025 and beyond.</p>
  3429.  
  3430.  
  3431.  
  3432. <p>For businesses prioritizing GDPR compliance, transparent pricing, and high-performance hosting within European borders, these top providers offer compelling options for every need and budget.</p>
  3433. ]]></content:encoded>
  3434. <wfw:commentRss>https://portalzine.de/best-server-hosts-in-europe-2025-the-ultimate-guide/feed/</wfw:commentRss>
  3435. <slash:comments>0</slash:comments>
  3436. </item>
  3437. <item>
  3438. <title>Visualize This: Open-Source Diagram Tools to Replace GoJS</title>
  3439. <link>https://portalzine.de/visualize-this-open-source-diagram-tools-to-replace-gojs/</link>
  3440. <comments>https://portalzine.de/visualize-this-open-source-diagram-tools-to-replace-gojs/#respond</comments>
  3441. <dc:creator><![CDATA[]]></dc:creator>
  3442. <pubDate>Sat, 12 Apr 2025 07:57:16 +0000</pubDate>
  3443. <category><![CDATA[Development]]></category>
  3444. <category><![CDATA[Javascript]]></category>
  3445. <guid isPermaLink="false">https://portalzine.de/?p=8395</guid>
  3446.  
  3447. <description><![CDATA[So you&#8217;re looking for a diagram library for your web project, and you&#8217;ve heard about GoJS. It&#8217;s powerful and feature-packed, but there&#8217;s one catch – it&#8217;s not free. While GoJS is undoubtedly excellent at what it does, its commercial licensing might not fit everyone&#8217;s budget or project requirements. The good news? There&#8217;s a vibrant ecosystem [&#8230;]]]></description>
  3448. <content:encoded><![CDATA[
  3449. <p>So you&#8217;re looking for a diagram library for your web project, and you&#8217;ve heard about GoJS. It&#8217;s powerful and feature-packed, but there&#8217;s one catch – it&#8217;s not free. While GoJS is undoubtedly excellent at what it does, its commercial licensing might not fit everyone&#8217;s budget or project requirements.</p>
  3450.  
  3451.  
  3452.  
  3453. <p>The good news? There&#8217;s a vibrant ecosystem of open-source alternatives that might just do the trick. Let&#8217;s dive in!</p>
  3454.  
  3455.  
  3456.  
  3457. <h2 class="wp-block-heading">What&#8217;s GoJS All About, Anyway?</h2>
  3458.  
  3459.  
  3460.  
  3461. <p>Before we explore the alternatives, let&#8217;s understand what makes GoJS so popular.</p>
  3462.  
  3463.  
  3464.  
  3465. <p>GoJS is a JavaScript library that helps you build interactive diagrams in the browser. Think flowcharts, org charts, BPMN diagrams, network visualizations – pretty much any visual representation of connected data. It&#8217;s packed with features like drag-and-drop interaction, undo/redo functionality, and various layout algorithms to automatically arrange your nodes.</p>
  3466.  
  3467.  
  3468.  
  3469. <p>Developed by Northwoods Software, GoJS offers:</p>
  3470.  
  3471.  
  3472.  
  3473. <ul class="wp-block-list">
  3474. <li>Super interactive diagrams (drag, click, copy, paste – you name it)</li>
  3475.  
  3476.  
  3477.  
  3478. <li>A model-view architecture that separates your data from its visual representation</li>
  3479.  
  3480.  
  3481.  
  3482. <li>Tons of customization options</li>
  3483.  
  3484.  
  3485.  
  3486. <li>Excellent documentation with over 200 interactive examples</li>
  3487.  
  3488.  
  3489.  
  3490. <li>Battle-tested stability for production applications</li>
  3491. </ul>
  3492.  
  3493.  
  3494.  
  3495. <p>It&#8217;s a fantastic tool, but if the licensing costs don&#8217;t work for you, let&#8217;s check out some worthy open-source contenders.</p>
  3496.  
  3497.  
  3498.  
  3499. <h2 class="wp-block-heading">Top Open-Source Alternatives to GoJS</h2>
  3500.  
  3501.  
  3502.  
  3503. <h3 class="wp-block-heading">1. JointJS (Community Edition)</h3>
  3504.  
  3505.  
  3506.  
  3507. <p>JointJS feels closest to GoJS in terms of general-purpose diagramming. It offers an open-source community edition that&#8217;s quite capable, alongside a commercial version with extra features.</p>
  3508.  
  3509.  
  3510.  
  3511. <p><strong>What&#8217;s awesome:</strong></p>
  3512.  
  3513.  
  3514.  
  3515. <ul class="wp-block-list">
  3516. <li>SVG-based for crisp, high-quality graphics</li>
  3517.  
  3518.  
  3519.  
  3520. <li>Flexible shape system for custom diagram elements</li>
  3521.  
  3522.  
  3523.  
  3524. <li>Works with any JavaScript framework</li>
  3525.  
  3526.  
  3527.  
  3528. <li>Solid documentation and examples</li>
  3529. </ul>
  3530.  
  3531.  
  3532.  
  3533. <p><strong>The catch:</strong></p>
  3534.  
  3535.  
  3536.  
  3537. <ul class="wp-block-list">
  3538. <li>The free version is somewhat limited compared to the paid JointJS+</li>
  3539.  
  3540.  
  3541.  
  3542. <li>Might require more coding for advanced interactions</li>
  3543. </ul>
  3544.  
  3545.  
  3546.  
  3547. <p><strong>Perfect for:</strong> Web applications that need standard diagramming features without breaking the bank.</p>
  3548.  
  3549.  
  3550.  
  3551. <p><a href="https://www.jointjs.com/" target="_blank" rel="noopener">Check out JointJS →</a></p>
  3552.  
  3553.  
  3554.  
  3555. <h3 class="wp-block-heading">2. Cytoscape.js</h3>
  3556.  
  3557.  
  3558.  
  3559. <p>If your diagrams lean more toward networks, relationships, and graph theory, Cytoscape.js is an absolute powerhouse.</p>
  3560.  
  3561.  
  3562.  
  3563. <p><strong>What&#8217;s awesome:</strong></p>
  3564.  
  3565.  
  3566.  
  3567. <ul class="wp-block-list">
  3568. <li>Blazing fast, even with thousands of elements</li>
  3569.  
  3570.  
  3571.  
  3572. <li>Uses WebGL for rendering large networks</li>
  3573.  
  3574.  
  3575.  
  3576. <li>Rich set of graph algorithms built in</li>
  3577.  
  3578.  
  3579.  
  3580. <li>Used by major tech companies like Amazon, Google, and Microsoft</li>
  3581.  
  3582.  
  3583.  
  3584. <li>Fantastic for scientific and research applications</li>
  3585. </ul>
  3586.  
  3587.  
  3588.  
  3589. <p><strong>The catch:</strong></p>
  3590.  
  3591.  
  3592.  
  3593. <ul class="wp-block-list">
  3594. <li>Steeper learning curve if you&#8217;re new to graph theory</li>
  3595.  
  3596.  
  3597.  
  3598. <li>Not as focused on business-style diagrams like org charts</li>
  3599. </ul>
  3600.  
  3601.  
  3602.  
  3603. <p><strong>Perfect for:</strong> Network analysis, relationship mapping, scientific visualizations, and any project dealing with complex interconnected data.</p>
  3604.  
  3605.  
  3606.  
  3607. <p><a href="https://js.cytoscape.org/" target="_blank" rel="noopener">Check out Cytoscape.js →</a></p>
  3608.  
  3609.  
  3610.  
  3611. <h3 class="wp-block-heading">3. React Flow</h3>
  3612.  
  3613.  
  3614.  
  3615. <p>If you&#8217;re a React developer, this one&#8217;s a no-brainer. React Flow is specifically designed for building node-based editors and interactive diagrams in React apps.</p>
  3616.  
  3617.  
  3618.  
  3619. <p><strong>What&#8217;s awesome:</strong></p>
  3620.  
  3621.  
  3622.  
  3623. <ul class="wp-block-list">
  3624. <li>Built specifically for React with hooks and components</li>
  3625.  
  3626.  
  3627.  
  3628. <li>Smooth interactions right out of the box</li>
  3629.  
  3630.  
  3631.  
  3632. <li>Elegant API that feels natural for React devs</li>
  3633.  
  3634.  
  3635.  
  3636. <li>Active development and growing community</li>
  3637.  
  3638.  
  3639.  
  3640. <li>Smart rendering optimizations (only updates what changes)</li>
  3641. </ul>
  3642.  
  3643.  
  3644.  
  3645. <p><strong>The catch:</strong></p>
  3646.  
  3647.  
  3648.  
  3649. <ul class="wp-block-list">
  3650. <li>React-specific, so not ideal if you&#8217;re using another framework</li>
  3651.  
  3652.  
  3653.  
  3654. <li>Relatively newer library compared to some others</li>
  3655. </ul>
  3656.  
  3657.  
  3658.  
  3659. <p><strong>Perfect for:</strong> React applications needing workflow editors, node-based interfaces, or any interactive diagram component.</p>
  3660.  
  3661.  
  3662.  
  3663. <p><a href="https://reactflow.dev/" target="_blank" rel="noopener">Check out React Flow →</a></p>
  3664.  
  3665.  
  3666.  
  3667. <h3 class="wp-block-heading">4. diagrams.net (formerly draw.io) / mxGraph</h3>
  3668.  
  3669.  
  3670.  
  3671. <p>When you need a complete diagramming solution rather than just a library, diagrams.net (powered by the open-source mxGraph library) delivers.</p>
  3672.  
  3673.  
  3674.  
  3675. <p><strong>What&#8217;s awesome:</strong></p>
  3676.  
  3677.  
  3678.  
  3679. <ul class="wp-block-list">
  3680. <li>Full-featured diagram editor that works standalone or embedded</li>
  3681.  
  3682.  
  3683.  
  3684. <li>Massive selection of shape libraries and templates</li>
  3685.  
  3686.  
  3687.  
  3688. <li>Desktop apps, web version, and embeddable components</li>
  3689.  
  3690.  
  3691.  
  3692. <li>Client-side processing for privacy (no data sent to servers)</li>
  3693.  
  3694.  
  3695.  
  3696. <li>Mature and widely used in production</li>
  3697. </ul>
  3698.  
  3699.  
  3700.  
  3701. <p><strong>The catch:</strong></p>
  3702.  
  3703.  
  3704.  
  3705. <ul class="wp-block-list">
  3706. <li>More of a complete application than just a library</li>
  3707.  
  3708.  
  3709.  
  3710. <li>mxGraph itself isn&#8217;t actively developed anymore (focus is on diagrams.net)</li>
  3711. </ul>
  3712.  
  3713.  
  3714.  
  3715. <p><strong>Perfect for:</strong> General diagramming needs, standalone applications, or when you want to embed a full diagram editor.</p>
  3716.  
  3717.  
  3718.  
  3719. <p><a href="https://app.diagrams.net/" target="_blank" rel="noopener">Try diagrams.net →</a> | <a href="https://github.com/jgraph/drawio" target="_blank" rel="noopener">diagrams.net GitHub →</a> | <a href="https://github.com/jgraph/mxgraph" target="_blank" rel="noopener">mxGraph GitHub →</a></p>
  3720.  
  3721.  
  3722.  
  3723. <h3 class="wp-block-heading">5. D3.js</h3>
  3724.  
  3725.  
  3726.  
  3727. <p>The venerable D3.js isn&#8217;t specifically a diagramming library, but it&#8217;s the Swiss Army knife of data visualization and can definitely create interactive diagrams.</p>
  3728.  
  3729.  
  3730.  
  3731. <p><strong>What&#8217;s awesome:</strong></p>
  3732.  
  3733.  
  3734.  
  3735. <ul class="wp-block-list">
  3736. <li>Unmatched flexibility and customization</li>
  3737.  
  3738.  
  3739.  
  3740. <li>Direct DOM manipulation for precise control</li>
  3741.  
  3742.  
  3743.  
  3744. <li>Incredible community support and examples</li>
  3745.  
  3746.  
  3747.  
  3748. <li>Powers some of the web&#8217;s most impressive visualizations</li>
  3749. </ul>
  3750.  
  3751.  
  3752.  
  3753. <p><strong>The catch:</strong></p>
  3754.  
  3755.  
  3756.  
  3757. <ul class="wp-block-list">
  3758. <li>Significant learning curve</li>
  3759.  
  3760.  
  3761.  
  3762. <li>Requires more code for basic functionality</li>
  3763.  
  3764.  
  3765.  
  3766. <li>You&#8217;ll need to build diagramming features yourself</li>
  3767. </ul>
  3768.  
  3769.  
  3770.  
  3771. <p><strong>Perfect for:</strong> Custom visualizations where you need complete control, unique diagrams that don&#8217;t fit standard patterns.</p>
  3772.  
  3773.  
  3774.  
  3775. <p><a href="https://d3js.org/" target="_blank" rel="noopener">Check out D3.js →</a></p>
  3776.  
  3777.  
  3778.  
  3779. <h3 class="wp-block-heading">6. Vis.js Network</h3>
  3780.  
  3781.  
  3782.  
  3783. <p>For straightforward network visualizations without the complexity of Cytoscape.js, Vis.js Network component is worth a look.</p>
  3784.  
  3785.  
  3786.  
  3787. <p><strong>What&#8217;s awesome:</strong></p>
  3788.  
  3789.  
  3790.  
  3791. <ul class="wp-block-list">
  3792. <li>Easy to get started with</li>
  3793.  
  3794.  
  3795.  
  3796. <li>Physics-based layouts that create natural-looking networks</li>
  3797.  
  3798.  
  3799.  
  3800. <li>Interactive nodes and edges with customizable appearance</li>
  3801.  
  3802.  
  3803.  
  3804. <li>Handles relatively large datasets efficiently</li>
  3805. </ul>
  3806.  
  3807.  
  3808.  
  3809. <p><strong>The catch:</strong></p>
  3810.  
  3811.  
  3812.  
  3813. <ul class="wp-block-list">
  3814. <li>Limited to network-style visualizations</li>
  3815.  
  3816.  
  3817.  
  3818. <li>Not as performant as Cytoscape.js for very large graphs</li>
  3819. </ul>
  3820.  
  3821.  
  3822.  
  3823. <p><strong>Perfect for:</strong> Simple to medium complexity network visualizations and relationship graphs.</p>
  3824.  
  3825.  
  3826.  
  3827. <p><a href="https://visjs.org/" target="_blank" rel="noopener">Check out Vis.js →</a></p>
  3828.  
  3829.  
  3830.  
  3831. <h2 class="wp-block-heading">Features Comparison, Part 1</h2>
  3832.  
  3833.  
  3834.  
  3835. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Feature" class="mtr-th-tag"><div class="mtr-cell-content">Feature</div></th><th data-mtr-content="GoJS" class="mtr-th-tag"><div class="mtr-cell-content">GoJS</div></th><th data-mtr-content="JointJS (Free)" class="mtr-th-tag"><div class="mtr-cell-content">JointJS (Free)</div></th><th data-mtr-content="Cytoscape.js" class="mtr-th-tag"><div class="mtr-cell-content">Cytoscape.js</div></th></tr></thead><tbody><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">License</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Commercial</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Open-source (MIT)</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Open-source (MIT)</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Rendering</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Canvas</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">SVG</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Canvas/WebGL</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Diagram Types</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Flowcharts, org charts, BPMN, etc.</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Basic diagrams</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Network/graph focused</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Interactive Editing</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Basic</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Layout Algorithms</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Multiple</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Basic</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Multiple</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Learning Curve</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Moderate</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Steep</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Documentation</div></td><td data-mtr-content="GoJS" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td><td data-mtr-content="JointJS (Free)" class="mtr-td-tag"><div class="mtr-cell-content">Good</div></td><td data-mtr-content="Cytoscape.js" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td></tr></tbody></table></figure>
  3836.  
  3837.  
  3838.  
  3839. <h2 class="wp-block-heading">Features Comparison, Part 2</h2>
  3840.  
  3841.  
  3842.  
  3843. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-thead-th"><thead><tr><th data-mtr-content="Feature" class="mtr-th-tag"><div class="mtr-cell-content">Feature</div></th><th data-mtr-content="React Flow" class="mtr-th-tag"><div class="mtr-cell-content">React Flow</div></th><th data-mtr-content="diagrams.net" class="mtr-th-tag"><div class="mtr-cell-content">diagrams.net</div></th><th data-mtr-content="D3.js" class="mtr-th-tag"><div class="mtr-cell-content">D3.js</div></th></tr></thead><tbody><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">License</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Open-source (MIT)</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Open-source (Apache)</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Open-source (BSD)</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Rendering</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">SVG/HTML</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">SVG</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">SVG</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Diagram Types</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Node-based</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Flowcharts, UML, etc.</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Custom</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Interactive Editing</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Requires coding</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Layout Algorithms</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Basic</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Multiple</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Requires coding</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Learning Curve</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Low (for React devs)</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Low</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Steep</div></td></tr><tr><td data-mtr-content="Feature" class="mtr-td-tag"><div class="mtr-cell-content">Documentation</div></td><td data-mtr-content="React Flow" class="mtr-td-tag"><div class="mtr-cell-content">Good</div></td><td data-mtr-content="diagrams.net" class="mtr-td-tag"><div class="mtr-cell-content">Good</div></td><td data-mtr-content="D3.js" class="mtr-td-tag"><div class="mtr-cell-content">Extensive</div></td></tr></tbody></table></figure>
  3844.  
  3845.  
  3846.  
  3847. <h2 class="wp-block-heading">Picking the Right Tool for Your Job</h2>
  3848.  
  3849.  
  3850.  
  3851. <p>Choosing the right alternative to GoJS really depends on what you&#8217;re trying to build:</p>
  3852.  
  3853.  
  3854.  
  3855. <ul class="wp-block-list">
  3856. <li><strong>Building a network analysis tool?</strong> Cytoscape.js is your best bet.</li>
  3857.  
  3858.  
  3859.  
  3860. <li><strong>Creating a React-based workflow editor?</strong> React Flow will save you tons of time.</li>
  3861.  
  3862.  
  3863.  
  3864. <li><strong>Need a complete diagramming solution?</strong> diagrams.net/draw.io has you covered.</li>
  3865.  
  3866.  
  3867.  
  3868. <li><strong>Creating custom, unique visualizations?</strong> D3.js gives you total control.</li>
  3869.  
  3870.  
  3871.  
  3872. <li><strong>Want something closest to GoJS?</strong> Try JointJS Community Edition.</li>
  3873.  
  3874.  
  3875.  
  3876. <li><strong>Just need simple network visualization?</strong> Vis.js Network will do the trick.</li>
  3877. </ul>
  3878.  
  3879.  
  3880.  
  3881. <p>Remember, the best library is the one that fits your specific needs, technical stack, and the complexity of diagrams you&#8217;re creating. Don&#8217;t be afraid to experiment with a couple of options before committing.</p>
  3882.  
  3883.  
  3884.  
  3885. <h2 class="wp-block-heading">The Bottom Line</h2>
  3886.  
  3887.  
  3888.  
  3889. <p>While GoJS is a fantastic library with comprehensive features, these open-source alternatives prove you don&#8217;t always need a commercial license to create impressive interactive diagrams. Each library has its own strengths and focus areas, so take some time to explore them based on your project requirements.</p>
  3890.  
  3891.  
  3892.  
  3893. <p>Happy diagramming!</p>
  3894.  
  3895.  
  3896.  
  3897. <hr class="wp-block-separator has-alpha-channel-opacity"/>
  3898.  
  3899.  
  3900.  
  3901. <p><strong>Additional Resources:</strong></p>
  3902.  
  3903.  
  3904.  
  3905. <ul class="wp-block-list">
  3906. <li><a href="https://alternativeto.net/software/gojs/?license=opensource" target="_blank" rel="noopener">AlternativeTo: GoJS Alternatives</a></li>
  3907.  
  3908.  
  3909.  
  3910. <li><a href="https://www.jointjs.com/blog/javascript-diagramming-libraries" target="_blank" rel="noopener">JavaScript Diagramming Libraries Comparison</a></li>
  3911.  
  3912.  
  3913.  
  3914. <li><a href="https://modeling-languages.com/javascript-drawing-libraries-diagrams/" target="_blank" rel="noopener">JavaScript Drawing Libraries for Diagrams</a></li>
  3915.  
  3916.  
  3917.  
  3918. <li><a href="https://github.com/xyflow/awesome-node-based-uis" target="_blank" rel="noopener">Node-Based UI Libraries Collection</a></li>
  3919. </ul>
  3920. ]]></content:encoded>
  3921. <wfw:commentRss>https://portalzine.de/visualize-this-open-source-diagram-tools-to-replace-gojs/feed/</wfw:commentRss>
  3922. <slash:comments>0</slash:comments>
  3923. </item>
  3924. <item>
  3925. <title>Sorry, Not Sorry: Why You Can&#8217;t Escape AI in Your Dev Life (And Why That&#8217;s Mostly Okay)</title>
  3926. <link>https://portalzine.de/sorry-not-sorry-why-you-cant-escape-ai-in-your-dev-life-and-why-thats-mostly-okay/</link>
  3927. <comments>https://portalzine.de/sorry-not-sorry-why-you-cant-escape-ai-in-your-dev-life-and-why-thats-mostly-okay/#respond</comments>
  3928. <dc:creator><![CDATA[]]></dc:creator>
  3929. <pubDate>Fri, 11 Apr 2025 09:54:34 +0000</pubDate>
  3930. <category><![CDATA[Development]]></category>
  3931. <category><![CDATA[AI]]></category>
  3932. <guid isPermaLink="false">https://portalzine.de/?p=8386</guid>
  3933.  
  3934. <description><![CDATA[Remember when the scariest thing about coding was forgetting a semicolon? Those were simpler times. Now your IDE is literally finishing your sentences like an overeager partner who may or may not be planning digital world domination. AI tools have crashed the dev party, and whether you&#8217;re grabbing the aux cord to blast &#8216;Welcome to [&#8230;]]]></description>
  3935. <content:encoded><![CDATA[
  3936. <p>Remember when the scariest thing about coding was forgetting a semicolon? Those were simpler times. Now your IDE is literally finishing your sentences like an overeager partner who may or may not be planning digital world domination. </p>
  3937.  
  3938.  
  3939.  
  3940. <p>AI tools have crashed the dev party, and whether you&#8217;re grabbing the aux cord to blast &#8216;Welcome to the Machine&#8217; or hiding in the bathroom hoping they&#8217;ll leave, one thing&#8217;s crystal clear: these algorithmic party crashers aren&#8217;t taking the hint. They&#8217;re here to stay, they&#8217;re reshaping everything, and honestly? The relationship status is&#8230; complicated.</p>
  3941.  
  3942.  
  3943.  
  3944. <h2 class="wp-block-heading">The Good Stuff: Why Some Devs Are Swiping Right</h2>
  3945.  
  3946.  
  3947.  
  3948. <p>Look, I get it—you&#8217;re sick of hearing how AI is going to change everything. But here&#8217;s the uncomfortable truth: the numbers don&#8217;t lie. According to <a href="https://www.deloitte.com/uk/en/Industries/technology/blogs/2024/the-future-of-coding-is-here-how-ai-is-reshaping-software-development.html" target="_blank" rel="noopener">Deloitte research</a>, AI can save developers up to a whopping 50% of their time on generic programming tasks. That&#8217;s like getting every other day off work while still getting paid. The 2024 <a href="https://middlewarehq.com/blog/how-ai-is-reshaping-software-development-insights-from-the-2024-dora-report" target="_blank" rel="noopener">DORA report</a> found teams using AI tools detected and fixed bugs up to 40% faster. And let&#8217;s be real—debugging is the coding equivalent of cleaning the bathroom. Anything that makes that process less painful deserves at least a golf clap.</p>
  3949.  
  3950.  
  3951.  
  3952. <p>Plenty of developers are already all-in on the AI hype train. They&#8217;re letting GitHub Copilot finish their sentences, having ChatGPT explain complex algorithms, and generally vibing with their new silicon buddies. For these early adopters, AI isn&#8217;t replacing their jobs—it&#8217;s eliminating the soul-crushing parts they never wanted to do anyway.</p>
  3953.  
  3954.  
  3955.  
  3956. <h2 class="wp-block-heading">The Not-So-Good Stuff: Red Flags in Your Digital Relationship</h2>
  3957.  
  3958.  
  3959.  
  3960. <p>But hold up—before you commit to this digital relationship, let&#8217;s talk red flags. Remember that perfect code GitHub Copilot generated for you? <a href="https://arxiv.org/html/2204.04741v5" target="_blank" rel="noopener">Research suggests</a> it might be churning out insecure code about 40% of the time. Yikes. A <a href="https://visualstudiomagazine.com/articles/2024/01/25/copilot-research.aspx" target="_blank" rel="noopener">recent study</a> even found &#8220;disconcerting trends for maintainability&#8221; in AI-generated code. Translation: that quick fix might become tomorrow&#8217;s maintenance nightmare.</p>
  3961.  
  3962.  
  3963.  
  3964. <p>Then there&#8217;s the whole &#8220;is this even legal?&#8221; question. Copilot was trained on billions of lines of code, much of it open-source. Some developers are raising eyebrows about intellectual property rights, wondering if their code is being regurgitated without credit. And don&#8217;t forget the privacy concerns—as <a href="https://www.kolide.com/blog/github-copilot-isn-t-worth-the-risk" target="_blank" rel="noopener">one critic pointedly observed</a>, &#8220;Copilot is a keylogger.&#8221; Everything you type could potentially become training data for the next iteration. Feeling comfortable yet?</p>
  3965.  
  3966.  
  3967.  
  3968. <h2 class="wp-block-heading">Hot Takes From Both Sides</h2>
  3969.  
  3970.  
  3971.  
  3972. <blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
  3973. <p>&#8220;I haven&#8217;t written a for-loop in six months. Copilot does it better than I ever did.&#8221; —AI Enthusiast</p>
  3974. </blockquote>
  3975.  
  3976.  
  3977.  
  3978. <blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
  3979. <p>&#8220;I&#8217;ll start using AI when it stops suggesting solutions that make me question its sanity.&#8221; —AI Skeptic</p>
  3980. </blockquote>
  3981.  
  3982.  
  3983.  
  3984. <blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
  3985. <p>&#8220;My team shipped in 3 weeks what used to take 3 months. The code quality? Let&#8217;s just say we&#8217;re keeping our QA team very busy.&#8221; —Pragmatic Manager</p>
  3986. </blockquote>
  3987.  
  3988.  
  3989.  
  3990. <h2 class="wp-block-heading">The Middle Ground: It&#8217;s Just Another Tool (But Like, a Really Fancy One)</h2>
  3991.  
  3992.  
  3993.  
  3994. <p>Maybe the truth is somewhere in between the utopian AI wonderland and the dystopian code-pocalypse. According to <a href="https://www.mckinsey.com/capabilities/quantumblack/our-insights/the-state-of-ai" target="_blank" rel="noopener">McKinsey&#8217;s 2024 survey</a>, 53% of executives report regularly using generative AI at work—but interestingly, fewer mid-level managers do. This suggests AI is still finding its place in the development workflow, not conquering it overnight.</p>
  3995.  
  3996.  
  3997.  
  3998. <p>The most level-headed take might be seeing AI as just another tool in an already crowded toolbox. Sure, it&#8217;s a shiny hammer that occasionally mistakes thumbs for nails, but it&#8217;s still just a hammer. The developers who thrive won&#8217;t be the ones who refuse to use AI or who blindly accept its every suggestion. They&#8217;ll be the ones who understand both its capabilities and limitations, who know when to leverage its strengths and when to rely on their own expertise.</p>
  3999.  
  4000.  
  4001.  
  4002. <h2 class="wp-block-heading">Why Resistance Is Probably Futile</h2>
  4003.  
  4004.  
  4005.  
  4006. <p>Here&#8217;s why fighting against AI in development is like trying to swim against a tsunami while wearing concrete shoes: the market has decided. <a href="https://www.forrester.com/blogs/predictions-2025-software-development/" target="_blank" rel="noopener">Forrester&#8217;s 2025 predictions</a> suggest AI will be embedded in every part of the software development process. Companies facing developer shortages are turning to AI to bridge the gap. Your competitors are already using these tools to ship faster. Love it or hate it, AI adoption in development isn&#8217;t slowing down—it&#8217;s accelerating.</p>
  4007.  
  4008.  
  4009.  
  4010. <p>The question isn&#8217;t whether AI will be part of development; it&#8217;s how you&#8217;ll adapt your workflow to incorporate it while mitigating its risks. Because while you&#8217;re debating whether to use it, someone else is figuring out how to use it better.</p>
  4011.  
  4012.  
  4013.  
  4014. <h2 class="wp-block-heading">Plot Twist: The Real Winners Aren&#8217;t Who You Think</h2>
  4015.  
  4016.  
  4017.  
  4018. <p>Here&#8217;s where things get interesting. The real winners in this AI revolution won&#8217;t be the evangelists who blindly trust everything the machine spits out. Nor will they be the purists who refuse to touch AI with a ten-foot keyboard.</p>
  4019.  
  4020.  
  4021.  
  4022. <p>The developers who&#8217;ll truly thrive will be the critical thinkers—those who can leverage AI&#8217;s speed while applying human judgment to its output. As Clark notes in <a href="https://www.dice.com/career-advice/how-ai-will-impact-software-development-in-2025-and-beyond" target="_blank" rel="noopener">this Dice article</a>, &#8220;Five years from now, AI will be embedded in every part of the software development process,&#8221; but it&#8217;s still going to be a co-pilot, not the captain.</p>
  4023.  
  4024.  
  4025.  
  4026. <p>Think less &#8220;AI will code for me&#8221; and more &#8220;AI will handle the boring parts so I can focus on the challenging, creative aspects that humans excel at.&#8221; The sweet spot is in the collaboration, not the replacement.</p>
  4027.  
  4028.  
  4029.  
  4030. <h2 class="wp-block-heading">So Where Does This Leave Us?</h2>
  4031.  
  4032.  
  4033.  
  4034. <p>So where does all this leave us mere mortals with our manually typed for-loops and caffeine dependencies? Probably somewhere between excitement and existential dread—which, if we&#8217;re being honest, is where most developers live anyway.</p>
  4035.  
  4036.  
  4037.  
  4038. <p>Here&#8217;s my take: AI isn&#8217;t going to replace developers any more than calculators replaced mathematicians. But it will change what being a developer means. The most valuable skills might shift from memorizing syntax to knowing which problems AI can solve and which ones require good old-fashioned human ingenuity.</p>
  4039.  
  4040.  
  4041.  
  4042. <p>The developers who thrive will be those who form a healthy relationship with AI—one where you&#8217;re neither rejecting its help nor letting it make all your decisions. Think of it as pair programming with a partner who&#8217;s simultaneously brilliant and occasionally hallucinates.</p>
  4043.  
  4044.  
  4045.  
  4046. <p>In the end, whether you&#8217;re an AI evangelist or a skeptic, there&#8217;s one point we can all agree on: this tech is reshaping our field, and the only truly risky move is pretending it isn&#8217;t happening. So maybe it&#8217;s time to swipe right on AI tools—just keep your eyes open, your code reviews thorough, and your sense of humor intact. After all, if the machines do eventually take over, you&#8217;ll want them to remember you were nice to them.</p>
  4047. ]]></content:encoded>
  4048. <wfw:commentRss>https://portalzine.de/sorry-not-sorry-why-you-cant-escape-ai-in-your-dev-life-and-why-thats-mostly-okay/feed/</wfw:commentRss>
  4049. <slash:comments>0</slash:comments>
  4050. </item>
  4051. <item>
  4052. <title>CSS Libraries That Are Actually Worth Your Time in 2025</title>
  4053. <link>https://portalzine.de/the-best-css-libraries-to-use-in-2025/</link>
  4054. <comments>https://portalzine.de/the-best-css-libraries-to-use-in-2025/#respond</comments>
  4055. <dc:creator><![CDATA[]]></dc:creator>
  4056. <pubDate>Fri, 11 Apr 2025 09:44:11 +0000</pubDate>
  4057. <category><![CDATA[Design]]></category>
  4058. <category><![CDATA[CSS]]></category>
  4059. <category><![CDATA[Development]]></category>
  4060. <category><![CDATA[SCSS]]></category>
  4061. <guid isPermaLink="false">https://portalzine.de/?p=1711</guid>
  4062.  
  4063. <description><![CDATA[Hey there, fellow developers! The CSS world keeps evolving, and it&#8217;s pretty wild how many awesome tools we have at our fingertips in 2025. Whether you&#8217;re building a quick side project or tackling a massive web app, these CSS frameworks and libraries can seriously level up your development game. Let&#8217;s dive into the ones that [&#8230;]]]></description>
  4064. <content:encoded><![CDATA[
  4065. <p>Hey there, fellow developers! The CSS world keeps evolving, and it&#8217;s pretty wild how many awesome tools we have at our fingertips in 2025. Whether you&#8217;re building a quick side project or tackling a massive web app, these CSS frameworks and libraries can seriously level up your development game. Let&#8217;s dive into the ones that are actually worth your time this year.</p>
  4066.  
  4067.  
  4068.  
  4069. <p><strong>BTW, I am really digging the MVP.css approach ;)</strong></p>
  4070.  
  4071.  
  4072.  
  4073. <h2 class="wp-block-heading">1. Tailwind CSS &#8211; Still the Utility King</h2>
  4074.  
  4075.  
  4076.  
  4077. <p><a href="https://tailwindcss.com" target="_blank" rel="noopener">https://tailwindcss.com</a></p>
  4078.  
  4079.  
  4080.  
  4081. <p>Tailwind is absolutely crushing it in 2025! This utility-first framework lets you slap together amazing designs right in your HTML without writing a single line of CSS. It&#8217;s basically developer catnip at this point.</p>
  4082.  
  4083.  
  4084.  
  4085. <p><strong>Why you&#8217;ll love it:</strong></p>
  4086.  
  4087.  
  4088.  
  4089. <ul class="wp-block-list">
  4090. <li>Build stuff crazy fast with utility classes</li>
  4091.  
  4092.  
  4093.  
  4094. <li>Customize everything through a simple config file</li>
  4095.  
  4096.  
  4097.  
  4098. <li>Tons of plugins from an awesome community</li>
  4099.  
  4100.  
  4101.  
  4102. <li>Perfect for complex UIs when you&#8217;re sick of writing custom CSS</li>
  4103.  
  4104.  
  4105.  
  4106. <li>Plays nice with React, Vue, Angular, or whatever framework you&#8217;re into</li>
  4107. </ul>
  4108.  
  4109.  
  4110.  
  4111. <h2 class="wp-block-heading">2. Bootstrap 6 &#8211; The Reliable Old Friend With New Tricks</h2>
  4112.  
  4113.  
  4114.  
  4115. <p><a href="https://getbootstrap.com" target="_blank" rel="noopener">https://getbootstrap.com</a></p>
  4116.  
  4117.  
  4118.  
  4119. <p>Bootstrap 6 is like that friend who keeps reinventing themselves. The latest version brings better CSS variables, accessibility improvements, and smoother integration with modern JS frameworks. It&#8217;s still the go-to when you need to ship something yesterday.</p>
  4120.  
  4121.  
  4122.  
  4123. <p><strong>Why it&#8217;s still awesome:</strong></p>
  4124.  
  4125.  
  4126.  
  4127. <ul class="wp-block-list">
  4128. <li>That grid system is <em>chef&#8217;s kiss</em> for responsive designs</li>
  4129.  
  4130.  
  4131.  
  4132. <li>Pre-built components that just work</li>
  4133.  
  4134.  
  4135.  
  4136. <li>Documentation that actually makes sense</li>
  4137.  
  4138.  
  4139.  
  4140. <li>Better CSS custom properties support</li>
  4141.  
  4142.  
  4143.  
  4144. <li>Works for both traditional sites and fancy modern apps</li>
  4145. </ul>
  4146.  
  4147.  
  4148.  
  4149. <h2 class="wp-block-heading">3. UnoCSS &#8211; The Speed Demon</h2>
  4150.  
  4151.  
  4152.  
  4153. <p><a href="https://unocss.dev" target="_blank" rel="noopener">https://unocss.dev</a></p>
  4154.  
  4155.  
  4156.  
  4157. <p>UnoCSS is the new kid on the block that&#8217;s turning heads. It&#8217;s an instant, on-demand atomic CSS engine that&#8217;s ridiculously fast and super flexible.</p>
  4158.  
  4159.  
  4160.  
  4161. <p><strong>Why it&#8217;s gaining fans:</strong></p>
  4162.  
  4163.  
  4164.  
  4165. <ul class="wp-block-list">
  4166. <li>Way faster than other atomic CSS frameworks</li>
  4167.  
  4168.  
  4169.  
  4170. <li>Customizable with preset configurations</li>
  4171.  
  4172.  
  4173.  
  4174. <li>Zero runtime overhead</li>
  4175.  
  4176.  
  4177.  
  4178. <li>Works with any framework or vanilla projects</li>
  4179.  
  4180.  
  4181.  
  4182. <li>Perfect for performance-obsessed developers</li>
  4183. </ul>
  4184.  
  4185.  
  4186.  
  4187. <h2 class="wp-block-heading">4. Open Props &#8211; CSS Variables on Steroids</h2>
  4188.  
  4189.  
  4190.  
  4191. <p><a href="https://open-props.style" target="_blank" rel="noopener">https://open-props.style</a></p>
  4192.  
  4193.  
  4194.  
  4195. <p>Open Props takes CSS custom properties to a whole new level. It&#8217;s basically a collection of carefully crafted variables that make your designs consistent without the bloat of a full framework.</p>
  4196.  
  4197.  
  4198.  
  4199. <p><strong>Why it&#8217;s worth checking out:</strong></p>
  4200.  
  4201.  
  4202.  
  4203. <ul class="wp-block-list">
  4204. <li>Drop-in design tokens that just work</li>
  4205.  
  4206.  
  4207.  
  4208. <li>Perfect for design systems</li>
  4209.  
  4210.  
  4211.  
  4212. <li>Mix and match only what you need</li>
  4213.  
  4214.  
  4215.  
  4216. <li>Zero opinion on your HTML structure</li>
  4217.  
  4218.  
  4219.  
  4220. <li>Works alongside any other framework</li>
  4221. </ul>
  4222.  
  4223.  
  4224.  
  4225. <h2 class="wp-block-heading">5. Chakra UI &#8211; The Accessibility Champion</h2>
  4226.  
  4227.  
  4228.  
  4229. <p><a href="https://chakra-ui.com" target="_blank" rel="noopener">https://chakra-ui.com</a></p>
  4230.  
  4231.  
  4232.  
  4233. <p>Chakra UI keeps climbing the popularity charts, especially for React devs who care about building accessible interfaces without the headache.</p>
  4234.  
  4235.  
  4236.  
  4237. <p><strong>Why people are loving it:</strong></p>
  4238.  
  4239.  
  4240.  
  4241. <ul class="wp-block-list">
  4242. <li>Accessibility features baked right in</li>
  4243.  
  4244.  
  4245.  
  4246. <li>Component-based styling that&#8217;s super intuitive</li>
  4247.  
  4248.  
  4249.  
  4250. <li>Theme support that actually makes sense</li>
  4251.  
  4252.  
  4253.  
  4254. <li>Dark mode without the drama</li>
  4255.  
  4256.  
  4257.  
  4258. <li>Less custom CSS = happier developers</li>
  4259. </ul>
  4260.  
  4261.  
  4262.  
  4263. <h2 class="wp-block-heading">6. Water.css &#8211; The Minimalist Dream</h2>
  4264.  
  4265.  
  4266.  
  4267. <p><a href="https://watercss.kognise.dev" target="_blank" rel="noopener">https://watercss.kognise.dev</a></p>
  4268.  
  4269.  
  4270.  
  4271. <p>Water.css is the definition of &#8220;less is more.&#8221; It&#8217;s a drop-in stylesheet that makes plain HTML look surprisingly good without any classes or configuration.</p>
  4272.  
  4273.  
  4274.  
  4275. <p><strong>Why it&#8217;s refreshing:</strong></p>
  4276.  
  4277.  
  4278.  
  4279. <ul class="wp-block-list">
  4280. <li>Zero classes needed &#8211; just write semantic HTML</li>
  4281.  
  4282.  
  4283.  
  4284. <li>Perfect for quick projects, docs, or prototypes</li>
  4285.  
  4286.  
  4287.  
  4288. <li>Light and dark themes built in</li>
  4289.  
  4290.  
  4291.  
  4292. <li>Ridiculously small file size</li>
  4293.  
  4294.  
  4295.  
  4296. <li>Makes your content shine, not your framework</li>
  4297. </ul>
  4298.  
  4299.  
  4300.  
  4301. <h2 class="wp-block-heading">7. MVP.css &#8211; HTML That Just Works</h2>
  4302.  
  4303.  
  4304.  
  4305. <p><a href="https://andybrewer.github.io/mvp" target="_blank" rel="noopener">https://andybrewer.github.io/mvp</a></p>
  4306.  
  4307.  
  4308.  
  4309. <p>Similar to Water.css, MVP.css is all about letting you focus on content instead of classes. Just write proper HTML, and it&#8217;ll look decent without any extra effort.</p>
  4310.  
  4311.  
  4312.  
  4313. <p><strong>Why it&#8217;s a game-changer:</strong></p>
  4314.  
  4315.  
  4316.  
  4317. <ul class="wp-block-list">
  4318. <li>No classes, no framework overhead</li>
  4319.  
  4320.  
  4321.  
  4322. <li>Responsive out of the box</li>
  4323.  
  4324.  
  4325.  
  4326. <li>Perfect for content-heavy sites</li>
  4327.  
  4328.  
  4329.  
  4330. <li>Makes semantic HTML look good by default</li>
  4331.  
  4332.  
  4333.  
  4334. <li>Ideal for quick projects when you don&#8217;t want to think about styling</li>
  4335. </ul>
  4336.  
  4337.  
  4338.  
  4339. <h2 class="wp-block-heading">8. DaisyUI &#8211; Tailwind&#8217;s Best Friend</h2>
  4340.  
  4341.  
  4342.  
  4343. <p><a href="https://daisyui.com" target="_blank" rel="noopener">https://daisyui.com</a></p>
  4344.  
  4345.  
  4346.  
  4347. <p>DaisyUI is like Tailwind&#8217;s cool cousin who brings components to the party. It extends Tailwind CSS with beautiful pre-designed components while keeping all the customization goodness.</p>
  4348.  
  4349.  
  4350.  
  4351. <p><strong>Why the combo works:</strong></p>
  4352.  
  4353.  
  4354.  
  4355. <ul class="wp-block-list">
  4356. <li>All of Tailwind&#8217;s utility power with ready-made components</li>
  4357.  
  4358.  
  4359.  
  4360. <li>Speeds up UI development like crazy</li>
  4361.  
  4362.  
  4363.  
  4364. <li>Fully customizable design system</li>
  4365.  
  4366.  
  4367.  
  4368. <li>Beautiful and accessible UIs without the hassle</li>
  4369.  
  4370.  
  4371.  
  4372. <li>Multiple themes including dark mode</li>
  4373. </ul>
  4374.  
  4375.  
  4376.  
  4377. <h2 class="wp-block-heading">9. Windi CSS &#8211; The Tailwind Alternative</h2>
  4378.  
  4379.  
  4380.  
  4381. <p><a href="https://windicss.org" target="_blank" rel="noopener">https://windicss.org</a></p>
  4382.  
  4383.  
  4384.  
  4385. <p>Windi CSS offers a fresh take on the utility-first approach, with on-demand generation and some unique features that Tailwind doesn&#8217;t have.</p>
  4386.  
  4387.  
  4388.  
  4389. <p><strong>Why some devs are switching:</strong></p>
  4390.  
  4391.  
  4392.  
  4393. <ul class="wp-block-list">
  4394. <li>Faster than traditional Tailwind in some cases</li>
  4395.  
  4396.  
  4397.  
  4398. <li>Supports arbitrary values without extra config</li>
  4399.  
  4400.  
  4401.  
  4402. <li>Variant groups for cleaner HTML</li>
  4403.  
  4404.  
  4405.  
  4406. <li>Attribute-based utilities</li>
  4407.  
  4408.  
  4409.  
  4410. <li>No PurgeCSS needed</li>
  4411. </ul>
  4412.  
  4413.  
  4414.  
  4415. <h2 class="wp-block-heading">10. Bulma &#8211; The Flexbox Master</h2>
  4416.  
  4417.  
  4418.  
  4419. <p><a href="https://bulma.io" target="_blank" rel="noopener">https://bulma.io</a></p>
  4420.  
  4421.  
  4422.  
  4423. <p>Bulma keeps things simple with its Flexbox-based approach. If you&#8217;re into clean, readable CSS and a straightforward layout system, Bulma&#8217;s still a solid choice in 2025.</p>
  4424.  
  4425.  
  4426.  
  4427. <p><strong>Why it works for many projects:</strong></p>
  4428.  
  4429.  
  4430.  
  4431. <ul class="wp-block-list">
  4432. <li>Zero JavaScript dependencies</li>
  4433.  
  4434.  
  4435.  
  4436. <li>Intuitive and flexible layouts</li>
  4437.  
  4438.  
  4439.  
  4440. <li>Code that actually makes sense when you read it</li>
  4441.  
  4442.  
  4443.  
  4444. <li>Easy to customize with Sass</li>
  4445.  
  4446.  
  4447.  
  4448. <li>Great docs for when you get stuck</li>
  4449. </ul>
  4450.  
  4451.  
  4452.  
  4453. <h2 class="wp-block-heading">11. Lightning CSS &#8211; The Performance Optimizer</h2>
  4454.  
  4455.  
  4456.  
  4457. <p><a href="https://lightningcss.dev" target="_blank" rel="noopener">https://lightningcss.dev</a></p>
  4458.  
  4459.  
  4460.  
  4461. <p>Lightning CSS isn&#8217;t a traditional framework but rather an extremely fast CSS parser, transformer, and bundler that can supercharge your existing styles.</p>
  4462.  
  4463.  
  4464.  
  4465. <p><strong>Why performance geeks love it:</strong></p>
  4466.  
  4467.  
  4468.  
  4469. <ul class="wp-block-list">
  4470. <li>Ridiculously fast processing</li>
  4471.  
  4472.  
  4473.  
  4474. <li>Better minification than traditional tools</li>
  4475.  
  4476.  
  4477.  
  4478. <li>Modern CSS features with automatic fallbacks</li>
  4479.  
  4480.  
  4481.  
  4482. <li>Works with any framework or vanilla CSS</li>
  4483.  
  4484.  
  4485.  
  4486. <li>Perfect for production optimization</li>
  4487. </ul>
  4488.  
  4489.  
  4490.  
  4491. <h2 class="wp-block-heading">12. Foundation &#8211; The Enterprise Option</h2>
  4492.  
  4493.  
  4494.  
  4495. <p><a href="https://get.foundation" target="_blank" rel="noopener">https://get.foundation</a></p>
  4496.  
  4497.  
  4498.  
  4499. <p>Foundation continues to be the go-to for serious enterprise projects that need rock-solid reliability and advanced responsive features.</p>
  4500.  
  4501.  
  4502.  
  4503. <p><strong>Why it&#8217;s still relevant:</strong></p>
  4504.  
  4505.  
  4506.  
  4507. <ul class="wp-block-list">
  4508. <li>Mobile-first approach that actually works</li>
  4509.  
  4510.  
  4511.  
  4512. <li>Flexible grid for complex layouts</li>
  4513.  
  4514.  
  4515.  
  4516. <li>Advanced responsive tools beyond breakpoints</li>
  4517.  
  4518.  
  4519.  
  4520. <li>Customizable through Sass</li>
  4521.  
  4522.  
  4523.  
  4524. <li>Built for performance and accessibility</li>
  4525. </ul>
  4526.  
  4527.  
  4528.  
  4529. <h2 class="wp-block-heading">13. Pico.css &#8211; The Semantic Minimalist</h2>
  4530.  
  4531.  
  4532.  
  4533. <p><a href="https://picocss.com" target="_blank" rel="noopener">https://picocss.com</a></p>
  4534.  
  4535.  
  4536.  
  4537. <p>Pico.css focuses on making native HTML elements look great without additional classes, perfect for when you want to keep things clean and semantic.</p>
  4538.  
  4539.  
  4540.  
  4541. <p><strong>Why minimalists love it:</strong></p>
  4542.  
  4543.  
  4544.  
  4545. <ul class="wp-block-list">
  4546. <li>Class-free styling that just works</li>
  4547.  
  4548.  
  4549.  
  4550. <li>Lightweight and fast</li>
  4551.  
  4552.  
  4553.  
  4554. <li>Modern web standards support</li>
  4555.  
  4556.  
  4557.  
  4558. <li>Elegant default styling without the bloat</li>
  4559.  
  4560.  
  4561.  
  4562. <li>Light/dark modes that respect user preferences</li>
  4563. </ul>
  4564.  
  4565.  
  4566.  
  4567. <h2 class="wp-block-heading">14. UIkit &#8211; The All-in-One Solution</h2>
  4568.  
  4569.  
  4570.  
  4571. <p><a href="https://getuikit.com" target="_blank" rel="noopener">https://getuikit.com</a></p>
  4572.  
  4573.  
  4574.  
  4575. <p>UIkit provides a comprehensive framework with powerful components and flexible layouts for developers who want a complete solution in one package.</p>
  4576.  
  4577.  
  4578.  
  4579. <p><strong>Why it&#8217;s a solid choice:</strong></p>
  4580.  
  4581.  
  4582.  
  4583. <ul class="wp-block-list">
  4584. <li>Modular architecture that keeps things organized</li>
  4585.  
  4586.  
  4587.  
  4588. <li>Rich component library for any UI need</li>
  4589.  
  4590.  
  4591.  
  4592. <li>Mobile-friendly from the ground up</li>
  4593.  
  4594.  
  4595.  
  4596. <li>Smooth animations without extra libraries</li>
  4597.  
  4598.  
  4599.  
  4600. <li>Professional-looking results with minimal effort</li>
  4601. </ul>
  4602.  
  4603.  
  4604.  
  4605. <h2 class="wp-block-heading">The Bottom Line</h2>
  4606.  
  4607.  
  4608.  
  4609. <p>The CSS landscape in 2025 is all about choice &#8211; whether you&#8217;re team utility-first, component-based, or minimalist, there&#8217;s something awesome waiting for you. My advice? Pick the one that matches your project&#8217;s needs and your personal style. Sometimes the &#8220;best&#8221; framework is simply the one that helps you ship faster and makes you happy while coding.</p>
  4610.  
  4611.  
  4612.  
  4613. <p><strong>Keep on styling!</strong></p>
  4614.  
  4615.  
  4616.  
  4617.  
  4618. ]]></content:encoded>
  4619. <wfw:commentRss>https://portalzine.de/the-best-css-libraries-to-use-in-2025/feed/</wfw:commentRss>
  4620. <slash:comments>0</slash:comments>
  4621. </item>
  4622. <item>
  4623. <title>lnav &#8211; A Powerful Tool for Log File Debugging</title>
  4624. <link>https://portalzine.de/lnav-a-powerful-tool-for-log-file-debugging/</link>
  4625. <comments>https://portalzine.de/lnav-a-powerful-tool-for-log-file-debugging/#respond</comments>
  4626. <dc:creator><![CDATA[]]></dc:creator>
  4627. <pubDate>Tue, 08 Apr 2025 09:24:41 +0000</pubDate>
  4628. <category><![CDATA[Development]]></category>
  4629. <category><![CDATA[Debugging]]></category>
  4630. <category><![CDATA[Deployment]]></category>
  4631. <guid isPermaLink="false">https://portalzine.de/?p=8376</guid>
  4632.  
  4633. <description><![CDATA[Updated article from 2019. When debugging any complex application, log files are your best friends. However, navigating through thousands of log entries can quickly become overwhelming. This is where lnav (Log Navigator) comes in. This powerful command-line utility transforms the log analysis experience, making it easier to identify problems and troubleshoot issues in various applications [&#8230;]]]></description>
  4634. <content:encoded><![CDATA[
  4635. <p><em>Updated article from 2019.</em></p>
  4636.  
  4637.  
  4638.  
  4639. <p>When debugging any complex application, log files are your best friends. However, navigating through thousands of log entries can quickly become overwhelming. This is where <strong>lnav</strong> (Log Navigator) comes in. This powerful command-line utility transforms the log analysis experience, making it easier to identify problems and troubleshoot issues in various applications and systems.</p>
  4640.  
  4641.  
  4642.  
  4643. <p>In this comprehensive guide, we&#8217;ll explore how to use lnav for efficient log file debugging, from basic usage to advanced log analysis techniques. We&#8217;ll use WordPress as one example, but the techniques apply to any application that generates log files.</p>
  4644.  
  4645.  
  4646.  
  4647. <h2 class="wp-block-heading">What is lnav?</h2>
  4648.  
  4649.  
  4650.  
  4651. <p>Lnav is a sophisticated log file navigator designed to enhance the way developers and system administrators interact with log files. Unlike traditional tools like <code>tail</code>, <code>grep</code>, or <code>less</code>, lnav offers a feature-rich environment specifically designed for log analysis.</p>
  4652.  
  4653.  
  4654.  
  4655. <h3 class="wp-block-heading">Key Features:</h3>
  4656.  
  4657.  
  4658.  
  4659. <ul class="wp-block-list">
  4660. <li><strong>Consolidated View</strong>: View multiple log files simultaneously in a single, chronologically ordered display</li>
  4661.  
  4662.  
  4663.  
  4664. <li><strong>Syntax Highlighting</strong>: Automatic colorization of log levels, timestamps, and other elements</li>
  4665.  
  4666.  
  4667.  
  4668. <li><strong>Real-time Monitoring</strong>: Watch logs update in real-time with automatic refreshing</li>
  4669.  
  4670.  
  4671.  
  4672. <li><strong>Powerful Filtering</strong>: Filter log entries using regular expressions</li>
  4673.  
  4674.  
  4675.  
  4676. <li><strong>SQL Queries</strong>: Run SQL queries against your logs to extract specific information</li>
  4677.  
  4678.  
  4679.  
  4680. <li><strong>Timestamps Navigation</strong>: Easily jump to specific time periods</li>
  4681.  
  4682.  
  4683.  
  4684. <li><strong>Customizable Formats</strong>: Support for custom log formats, including WordPress</li>
  4685.  
  4686.  
  4687.  
  4688. <li><strong>Bookmarking</strong>: Mark important log entries for later reference</li>
  4689. </ul>
  4690.  
  4691.  
  4692.  
  4693. <h2 class="wp-block-heading">Installing lnav</h2>
  4694.  
  4695.  
  4696.  
  4697. <p>Before diving into WordPress debugging, let&#8217;s get lnav installed on your system.</p>
  4698.  
  4699.  
  4700.  
  4701. <h3 class="wp-block-heading">Linux</h3>
  4702.  
  4703.  
  4704.  
  4705. <p><strong>Debian/Ubuntu</strong>:</p>
  4706.  
  4707.  
  4708.  
  4709. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo apt update
  4710. sudo apt install lnav</pre></div>
  4711.  
  4712.  
  4713.  
  4714. <p><strong>Fedora</strong>:</p>
  4715.  
  4716.  
  4717.  
  4718. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo dnf install lnav</pre></div>
  4719.  
  4720.  
  4721.  
  4722. <p><strong>CentOS/RHEL</strong>:</p>
  4723.  
  4724.  
  4725.  
  4726. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo yum install lnav</pre></div>
  4727.  
  4728.  
  4729.  
  4730. <p><strong>Arch Linux</strong>:</p>
  4731.  
  4732.  
  4733.  
  4734. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">sudo pacman -S lnav</pre></div>
  4735.  
  4736.  
  4737.  
  4738. <h3 class="wp-block-heading">macOS</h3>
  4739.  
  4740.  
  4741.  
  4742. <p>Using Homebrew:</p>
  4743.  
  4744.  
  4745.  
  4746. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">brew install lnav</pre></div>
  4747.  
  4748.  
  4749.  
  4750. <h3 class="wp-block-heading">Windows</h3>
  4751.  
  4752.  
  4753.  
  4754. <p>Using Chocolatey:</p>
  4755.  
  4756.  
  4757.  
  4758. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">choco install lnav</pre></div>
  4759.  
  4760.  
  4761.  
  4762. <p>Alternatively, download binaries from the <a href="https://github.com/tstack/lnav/releases" target="_blank" rel="noopener">lnav GitHub releases page</a>.</p>
  4763.  
  4764.  
  4765.  
  4766. <h2 class="wp-block-heading">Configuring Applications for Logging</h2>
  4767.  
  4768.  
  4769.  
  4770. <p>Before using lnav, you need to ensure your applications are configured to generate detailed logs. Here&#8217;s an example of setting up WordPress for logging, which demonstrates a typical configuration process:</p>
  4771.  
  4772.  
  4773.  
  4774. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">// Enable debugging
  4775. define('WP_DEBUG', true);
  4776.  
  4777. // Enable debug logging to wp-content/debug.log
  4778. define('WP_DEBUG_LOG', true);
  4779.  
  4780. // Display errors on page (set to false in production)
  4781. define('WP_DEBUG_DISPLAY', false);
  4782.  
  4783. // For development: Disable JavaScript concatenation
  4784. define('SCRIPT_DEBUG', true);</pre></div>
  4785.  
  4786.  
  4787.  
  4788. <p>This WordPress configuration creates a <code>debug.log</code> file that captures PHP errors, warnings, and notices. Other applications will have their own logging configuration methods, but the general principle is to enable detailed logging to files that lnav can read.</p>
  4789.  
  4790.  
  4791.  
  4792. <h3 class="wp-block-heading">Other Common Logging Configurations</h3>
  4793.  
  4794.  
  4795.  
  4796. <p>Different applications and systems have their own logging mechanisms:</p>
  4797.  
  4798.  
  4799.  
  4800. <ul class="wp-block-list">
  4801. <li><strong>Apache/Nginx</strong>: These web servers typically log to access.log and error.log files</li>
  4802.  
  4803.  
  4804.  
  4805. <li><strong>MySQL/MariaDB</strong>: Database logs can be configured to capture slow queries and errors</li>
  4806.  
  4807.  
  4808.  
  4809. <li><strong>System logs</strong>: Linux systems log to /var/log/syslog or through journald</li>
  4810.  
  4811.  
  4812.  
  4813. <li><strong>Application frameworks</strong>: Most frameworks like Laravel, Django, and Spring have configurable logging systems</li>
  4814. </ul>
  4815.  
  4816.  
  4817.  
  4818. <h2 class="wp-block-heading">Basic lnav Usage</h2>
  4819.  
  4820.  
  4821.  
  4822. <p>Let&#8217;s start with the basics of using lnav to analyze WordPress logs.</p>
  4823.  
  4824.  
  4825.  
  4826. <h3 class="wp-block-heading">Opening Log Files</h3>
  4827.  
  4828.  
  4829.  
  4830. <p>To open any log file with lnav, simply run:</p>
  4831.  
  4832.  
  4833.  
  4834. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">lnav /path/to/logfile.log</pre></div>
  4835.  
  4836.  
  4837.  
  4838. <p>One of lnav&#8217;s greatest strengths is the ability to monitor multiple log files simultaneously:</p>
  4839.  
  4840.  
  4841.  
  4842. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">lnav /var/log/apache2/access.log /var/log/apache2/error.log /var/log/mysql/error.log</pre></div>
  4843.  
  4844.  
  4845.  
  4846. <p>For example, with a WordPress site, you might monitor:</p>
  4847.  
  4848.  
  4849.  
  4850. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">lnav /path/to/wp-content/debug.log /var/log/apache2/error.log /var/log/php/errors.log</pre></div>
  4851.  
  4852.  
  4853.  
  4854. <p>This combined view helps you correlate events across different components of your application stack, making it easier to track issues that span multiple systems.</p>
  4855.  
  4856.  
  4857.  
  4858. <h3 class="wp-block-heading">Essential lnav Commands</h3>
  4859.  
  4860.  
  4861.  
  4862. <p>Once inside lnav, you&#8217;ll need these basic commands:</p>
  4863.  
  4864.  
  4865.  
  4866. <figure class="wp-block-table"><table class="has-fixed-layout mtr-table mtr-tr-th"><tbody><tr><th data-mtr-content="Key" class="mtr-th-tag"><div class="mtr-cell-content">Key</div></th><th data-mtr-content="Action" class="mtr-th-tag"><div class="mtr-cell-content">Action</div></th></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>?</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">View help</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>q</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Quit</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>e</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Examine current line in text editor</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>i</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Enter interactive command mode</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>/</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Search forwards</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>n</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Move to next search hit</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>N</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Move to previous search hit</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>f</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Filter lines using a regular expression</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>F</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Clear active filters</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>r</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Toggle between relative/absolute timestamps</div></td></tr><tr><td data-mtr-content="Key" class="mtr-td-tag"><div class="mtr-cell-content"><code>;</code></div></td><td data-mtr-content="Action" class="mtr-td-tag"><div class="mtr-cell-content">Enter SQL query mode</div></td></tr></tbody></table></figure>
  4867.  
  4868.  
  4869.  
  4870. <h2 class="wp-block-heading">Practical Examples for Log Debugging</h2>
  4871.  
  4872.  
  4873.  
  4874. <p>Let&#8217;s look at some practical examples of using lnav for various debugging scenarios.</p>
  4875.  
  4876.  
  4877.  
  4878. <h3 class="wp-block-heading">Example 1: Filtering Specific Components</h3>
  4879.  
  4880.  
  4881.  
  4882. <p>If you suspect a particular component is causing issues, you can filter the logs to show only relevant entries:</p>
  4883.  
  4884.  
  4885.  
  4886. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag"># Start lnav with your log files
  4887. lnav /path/to/application.log
  4888.  
  4889. # Once inside lnav, press 'f' and type a filter:
  4890. f database
  4891.  
  4892. # Filter for a specific component
  4893. f authentication
  4894.  
  4895. # In a WordPress context, filter for a specific plugin:
  4896. f wp-content/plugins/specific-plugin-name
  4897.  
  4898. # Clear filter by pressing 'F'</pre></div>
  4899.  
  4900.  
  4901.  
  4902. <h3 class="wp-block-heading">Example 2: Using SQL Queries for Log Analysis</h3>
  4903.  
  4904.  
  4905.  
  4906. <p>Lnav&#8217;s SQL capabilities are extremely powerful for extracting insights from your logs:</p>
  4907.  
  4908.  
  4909.  
  4910. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag"># Press ';' to enter SQL mode, then type queries like:
  4911.  
  4912. # Count errors by type
  4913. ;SELECT log_level, COUNT(*) FROM access_log GROUP BY log_level
  4914.  
  4915. # Find the most frequent errors
  4916. ;SELECT message, COUNT(*) AS count
  4917. FROM error_log
  4918. GROUP BY message
  4919. ORDER BY count DESC
  4920. LIMIT 10
  4921.  
  4922. # Find events during a specific time period
  4923. ;SELECT * FROM syslog
  4924. WHERE log_time BETWEEN '2023-04-01 00:00:00' AND '2023-04-01 23:59:59'
  4925.  
  4926. # For WordPress logs, you could look for errors in specific plugin files:
  4927. ;SELECT * FROM debug_log
  4928. WHERE file_path LIKE '%plugin-name%'</pre></div>
  4929.  
  4930.  
  4931.  
  4932. <h3 class="wp-block-heading">Example 3: Advanced Filtering Techniques</h3>
  4933.  
  4934.  
  4935.  
  4936. <p>You can use regular expressions for more sophisticated filtering:</p>
  4937.  
  4938.  
  4939.  
  4940. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag"># Multiple conditions with regex
  4941. f (ERROR|FATAL)
  4942.  
  4943. # Finding database errors
  4944. f database error
  4945.  
  4946. # Finding specific error types (PHP example)
  4947. f PHP (Fatal|Parse) error
  4948.  
  4949. # Finding errors after a certain time
  4950. f '2023-04-01 14:00:00'
  4951.  
  4952. # For WordPress, finding specific hooks:
  4953. f 'apply_filters|do_action'</pre></div>
  4954.  
  4955.  
  4956.  
  4957. <h3 class="wp-block-heading">Example 4: Monitoring During Critical Operations</h3>
  4958.  
  4959.  
  4960.  
  4961. <p>System updates, deployments, and other critical operations can be risky. Set up monitoring before performing them:</p>
  4962.  
  4963.  
  4964.  
  4965. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag"># Set up monitoring before performing operations
  4966. lnav /var/log/syslog /var/log/auth.log /var/log/application.log
  4967.  
  4968. # For a web application like WordPress:
  4969. lnav /path/to/wp-content/debug.log /var/log/apache2/error.log
  4970.  
  4971. # Keep lnav running during the operation
  4972. # Press 'r' to toggle between relative and absolute timestamps
  4973. # Press 'p' to pause automatic tailing if needed</pre></div>
  4974.  
  4975.  
  4976.  
  4977. <h2 class="wp-block-heading">Customizing lnav for Different Log Formats</h2>
  4978.  
  4979.  
  4980.  
  4981. <p>Most applications produce logs in specific formats. You can enhance lnav&#8217;s capabilities by creating custom format definitions for your applications.</p>
  4982.  
  4983.  
  4984.  
  4985. <h3 class="wp-block-heading">Example: WordPress Custom Format</h3>
  4986.  
  4987.  
  4988.  
  4989. <p>Here&#8217;s an example of creating a custom format for WordPress logs. Create or edit <code>~/.lnav/formats/wordpress.json</code>:</p>
  4990.  
  4991.  
  4992.  
  4993. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">{
  4994.  "wordpress_debug": {
  4995.    "title": "WordPress Debug Log",
  4996.    "description": "Format for WordPress debug.log",
  4997.    "url": "https://developer.wordpress.org/",
  4998.    "regex": {
  4999.      "standard": {
  5000.        "pattern": "\\[(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})\\] PHP (\\w+): (.+) in (.+) on line (\\d+)"
  5001.      }
  5002.    },
  5003.    "timestamp-format": [
  5004.      "%Y-%m-%d %H:%M:%S"
  5005.    ],
  5006.    "level-field": "level",
  5007.    "level": {
  5008.      "error": "Fatal error|Parse error",
  5009.      "warning": "Warning|Notice",
  5010.      "info": "Deprecated"
  5011.    },
  5012.    "value": {
  5013.      "timestamp": { "kind": "string", "identifier": true },
  5014.      "level": { "kind": "string", "identifier": true },
  5015.      "message": { "kind": "string" },
  5016.      "file": { "kind": "string" },
  5017.      "line": { "kind": "integer" }
  5018.    },
  5019.    "sample": [
  5020.      "[2023-04-01 12:34:56] PHP Warning: file_get_contents(): failed to open stream in /var/www/html/wp-content/plugins/example/file.php on line 123"
  5021.    ]
  5022.  }
  5023. }</pre></div>
  5024.  
  5025.  
  5026.  
  5027. <p>Custom format definitions like this help lnav properly parse and display application-specific logs, enabling better filtering and SQL queries.</p>
  5028.  
  5029.  
  5030.  
  5031. <h3 class="wp-block-heading">Creating Custom Formats for Other Applications</h3>
  5032.  
  5033.  
  5034.  
  5035. <p>The same approach can be used for any application with a consistent log format. The key elements to define are:</p>
  5036.  
  5037.  
  5038.  
  5039. <ul class="wp-block-list">
  5040. <li>The regex pattern that matches log lines</li>
  5041.  
  5042.  
  5043.  
  5044. <li>The timestamp format for proper time-based navigation</li>
  5045.  
  5046.  
  5047.  
  5048. <li>Log levels for proper coloring and filtering</li>
  5049.  
  5050.  
  5051.  
  5052. <li>Named capturing groups for SQL access</li>
  5053. </ul>
  5054.  
  5055.  
  5056.  
  5057. <h2 class="wp-block-heading">Common Error Patterns in Different Applications</h2>
  5058.  
  5059.  
  5060.  
  5061. <p>Understanding common error patterns helps you create effective filters in lnav. Here are some examples from various applications:</p>
  5062.  
  5063.  
  5064.  
  5065. <h3 class="wp-block-heading">PHP/WordPress Errors</h3>
  5066.  
  5067.  
  5068.  
  5069. <ol class="wp-block-list">
  5070. <li><strong>Undefined Variables</strong>:</li>
  5071. </ol>
  5072.  
  5073.  
  5074.  
  5075. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">PHP Notice: Undefined variable: variable_name</pre></div>
  5076.  
  5077.  
  5078.  
  5079. <ul class="wp-block-list">
  5080. <li><strong>Memory Exhaustion</strong>:</li>
  5081. </ul>
  5082.  
  5083.  
  5084.  
  5085. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">PHP Fatal error: Allowed memory size of X bytes exhausted</pre></div>
  5086.  
  5087.  
  5088.  
  5089. <h3 class="wp-block-heading">Web Server Errors</h3>
  5090.  
  5091.  
  5092.  
  5093. <ol class="wp-block-list">
  5094. <li><strong>Apache 404 Errors</strong>:</li>
  5095. </ol>
  5096.  
  5097.  
  5098.  
  5099. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">GET /missing-page HTTP/1.1" 404</pre></div>
  5100.  
  5101.  
  5102.  
  5103. <ul class="wp-block-list">
  5104. <li><strong>Permission Issues</strong>:</li>
  5105. </ul>
  5106.  
  5107.  
  5108.  
  5109. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">AH00035: access to /protected denied (filesystem path '/var/www/html/protected')</pre></div>
  5110.  
  5111.  
  5112.  
  5113. <h3 class="wp-block-heading">Database Errors</h3>
  5114.  
  5115.  
  5116.  
  5117. <ol class="wp-block-list">
  5118. <li><strong>Connection Failures</strong>:</li>
  5119. </ol>
  5120.  
  5121.  
  5122.  
  5123. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">ERROR 1040 (HY000): Too many connections</pre></div>
  5124.  
  5125.  
  5126.  
  5127. <ul class="wp-block-list">
  5128. <li><strong>Query Errors</strong>:</li>
  5129. </ul>
  5130.  
  5131.  
  5132.  
  5133. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">ERROR 1054 (42S22): Unknown column in field list</pre></div>
  5134.  
  5135.  
  5136.  
  5137. <h2 class="wp-block-heading">Advanced Debugging Workflows</h2>
  5138.  
  5139.  
  5140.  
  5141. <p>Here are some advanced workflows for debugging application issues with lnav:</p>
  5142.  
  5143.  
  5144.  
  5145. <h3 class="wp-block-heading">Workflow 1: Tracing a User Experience Issue</h3>
  5146.  
  5147.  
  5148.  
  5149. <ol class="wp-block-list">
  5150. <li>Enable detailed logging in your application</li>
  5151.  
  5152.  
  5153.  
  5154. <li>Reproduce the issue while monitoring logs across multiple systems:<br></li>
  5155. </ol>
  5156.  
  5157.  
  5158.  
  5159. <div class="wp-block-urvanov-syntax-highlighter-code-block"><pre class="urvanov-syntax-highlighter-plain-tag">lnav /var/log/application.log /var/log/web-server/access.log /var/log/web-server/error.log</pre></div>
  5160.  
  5161.  
  5162.  
  5163. <h2 class="wp-block-heading">Thoughts</h2>
  5164.  
  5165.  
  5166.  
  5167. <p>Lnav transforms the often tedious task of log analysis into a more efficient and insightful process. For WordPress developers and administrators, it provides a powerful way to debug issues, monitor performance, and understand the inner workings of their sites.</p>
  5168.  
  5169.  
  5170.  
  5171. <p>By mastering lnav&#8217;s filtering, SQL capabilities, and navigation features, you can dramatically reduce the time needed to troubleshoot problems and gain deeper insights into your WordPress installation&#8217;s behavior.</p>
  5172.  
  5173.  
  5174.  
  5175. <p>Whether you&#8217;re dealing with plugin conflicts, performance issues, or mysterious errors, lnav equips you with the tools needed to cut through the noise and find the answers hidden in your logs.</p>
  5176.  
  5177.  
  5178.  
  5179. <h2 class="wp-block-heading">Additional Resources</h2>
  5180.  
  5181.  
  5182.  
  5183. <ul class="wp-block-list">
  5184. <li><a href="https://lnav.org/" target="_blank" rel="noopener">Official lnav documentation</a></li>
  5185.  
  5186.  
  5187.  
  5188. <li><a href="https://github.com/tstack/lnav" target="_blank" rel="noopener">GitHub repository</a></li>
  5189.  
  5190.  
  5191.  
  5192. <li><a href="https://wordpress.org/documentation/article/debugging-in-wordpress/" target="_blank" rel="noopener">WordPress Debugging Documentation</a></li>
  5193. </ul>
  5194. ]]></content:encoded>
  5195. <wfw:commentRss>https://portalzine.de/lnav-a-powerful-tool-for-log-file-debugging/feed/</wfw:commentRss>
  5196. <slash:comments>0</slash:comments>
  5197. </item>
  5198. </channel>
  5199. </rss>
  5200.  
  5201. <!-- plugin=object-cache-pro client=phpredis metric#hits=9316 metric#misses=66 metric#hit-ratio=99.3 metric#bytes=1727414 metric#prefetches=0 metric#store-reads=217 metric#store-writes=23 metric#store-hits=263 metric#store-misses=19 metric#sql-queries=162 metric#ms-total=532.68 metric#ms-cache=9.71 metric#ms-cache-avg=0.0406 metric#ms-cache-ratio=1.8 -->
  5202.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=https%3A//portalzine.de/feed%3Fclean

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda