<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Guardioz on Medium]]></title>
        <description><![CDATA[Stories by Guardioz on Medium]]></description>
        <link>https://medium.com/@admin_7868?source=rss-bfdcfef1f708------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*1htdhkHV2SbTRQ8PYQuaSQ.jpeg</url>
            <title>Stories by Guardioz on Medium</title>
            <link>https://medium.com/@admin_7868?source=rss-bfdcfef1f708------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 18:26:21 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@admin_7868/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How PostgreSQL log_hostname Can Slow Down New Connections]]></title>
            <link>https://medium.com/@admin_7868/how-postgresql-log-hostname-can-slow-down-new-connections-ac6511a44b46?source=rss-bfdcfef1f708------2</link>
            <guid isPermaLink="false">https://medium.com/p/ac6511a44b46</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[dns]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[postgresql]]></category>
            <dc:creator><![CDATA[Guardioz]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 11:13:00 GMT</pubDate>
            <atom:updated>2026-11-04T11:13:00.159Z</atom:updated>
            <content:encoded><![CDATA[<p>November 2026</p><p>New TCP connections can take longer when PostgreSQL’s log_hostname setting is enabled. It adds a reverse DNS lookup before authentication, so the delay can range from milliseconds to seconds, depending on DNS. The %h escape in log_line_prefix can record the client’s IP address without a lookup; log_hostname changes it to a hostname obtained through DNS. Guardioz’s PostgreSQL 18 tests show how the settings work together and what that lookup cost when DNS was healthy, slow, or unavailable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/717/1*q4T4HKloSVIvRezKAPZxxw.png" /></figure><p>These three entries come from one connection in the original PostgreSQL 18.6 test. The prefix ends with %h. The address is the client’s IP, not the server’s, and PostgreSQL recorded it without a DNS lookup.</p><h3>Two settings that work together</h3><p>The server log can identify where a connection came from. Two settings control this information, but they have different roles.</p><p>🟣 <strong>%h</strong> is an escape in <strong>log_line_prefix</strong>, the text PostgreSQL places at the beginning of each log line. It represents the remote host name or IP address. On lines belonging to a client session, it shows where that client connected from.</p><p>🟣 <strong>log_hostname</strong> is a switch, disabled by default. It determines the value printed by <strong>%h</strong>: the client’s IP address when off, or a hostname obtained through DNS when on.</p><p>PostgreSQL 18’s documentation (<a href="https://www.postgresql.org/docs/18/runtime-config-logging.html">PostgreSQL: Documentation: 18: 19.8. Error Reporting and Logging</a>) warns that hostname resolution can impose a non-negligible performance cost, depending on the resolution setup.</p><p>Two practical details are important. log_hostname does not introduce its own field. Without<strong> %h</strong>, or <strong>%r</strong> to include the port, ordinary log lines do not identify the client, regardless of <strong>log_hostname</strong>. The upstream default prefix, ‘<strong>%m [%p] </strong>‘, contains neither. The switch also changes <strong>host=</strong> in the “connection received” message when connection logging is enabled, and populates <strong>pg_stat_activity.client_hostname.</strong> In the reported tests, the hostname replaced the IP address in the log rather than appearing beside it.</p><h3>What the lookup does, and when it runs</h3><p>A reverse DNS lookup asks which name is associated with an IP address, using a PTR record. With log_hostname enabled, PostgreSQL performs it for every new TCP connection immediately after that connection’s server process starts, before reading the first client message and before authentication. The connection waits for DNS to respond or for the system resolver to stop trying. Local Unix-socket connections do not perform this lookup.</p><p>The lookup runs whenever the switch is on, even if no part of the configuration prints the name. Its duration is also outside <strong>authentication_timeout.</strong> A comment beside the lookup in the PostgreSQL 18 source (<a href="https://github.com/postgres/postgres/blob/REL_18_STABLE/src/backend/tcop/backend_startup.c">postgres/src/backend/tcop/backend_startup.c at REL_18_STABLE · postgres/postgres</a>) explicitly excludes PreAuthDelay and the preceding DNS interactions from that time limit.</p><h3>The cost with healthy DNS</h3><p>The original measurements used two Microsoft Azure virtual machines in one private network: a PostgreSQL 18.6 server on Ubuntu 24.04, Standard_D4s_v5 with 4 vCPUs, and a separate Standard_D2s_v5 client. Connections used TLS 1.3 and SCRAM-SHA-256 password authentication, as many production environments do. The client ran pgbench, PostgreSQL’s bundled benchmark tool, with a custom script containing <strong>SELECT 1 </strong>and the <strong>-C</strong> option. This opens a new connection for each transaction, making connection setup most of the work. That is the purpose of this experiment, not the throughput of a real application workload.</p><p>Azure DNS returned vm-logtalk-hostname-cli.internal.cloudapp.net for the client’s address. Ubuntu uses systemd-resolved as a local DNS cache by default. Without a local cache, each lookup reaches the DNS server, so the database host was tested both with and without caching.</p><p>The table reports two values from pgbench. Connection time is the average duration of opening a connection. Connections per second counts how often the client connected, executed SELECT 1, and disconnected. pgbench calls this transactions per second; with -C, each transaction corresponds to one connection. With a single client, the next connection begins only after the preceding one ends. Longer connections therefore mean fewer connections per second. This is the rate of one client repeatedly reconnecting, not the maximum rate the server can accept.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/709/1*NEDu-yXkiUFUJdUUFtKK4g.png" /></figure><p>Without a local cache, each connection took about 6 ms longer: 43% more time per connection and 29% fewer connections per second in this test. Server-side timing supported the same result. PostgreSQL 18 can report setup duration through log_connections with setup_durations. Average setup time increased from 10.4 ms to 16.3 ms, while authentication remained at 6.0 ms in all three variants. The additional time occurred before authentication, where the DNS lookup runs.</p><p>With the local cache, the difference was 0.3 ms. Azure DNS assigned the answer a 10-second TTL, so most lookups were served from the cache. The roughly 6 ms represented a round trip to Azure DNS from that server. Another network will have a different figure: the time its DNS server takes to reply.</p><h3>The cost with slow or failing DNS</h3><p>Healthy DNS is the best case. The original experiment next delayed DNS and then made it unreachable, affecting only DNS traffic at the database server, before repeating the same one-client test.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/714/1*4kY2YHft7XXr0J3TbOK5jw.png" /></figure><p>With log_hostname off, DNS problems did not affect this test because no lookup was needed. With it on, every new connection waited. Delaying DNS by 100 ms added about 109 ms per connection. Around 106 ms was the lookup: the added 100 ms plus the approximately 6 ms observed with healthy DNS. The remaining 2–3 ms were distributed across other setup work; a single run cannot explain that difference. When no DNS response arrived, a connection waited until the resolver stopped trying. Direct DNS requests from glibc 2.39 on Ubuntu 24.04 took 20 seconds, with four requests sent five seconds apart. Through systemd-resolved, the wait was 10 seconds. Resolver options can change those durations, but the dependency remains: connection setup waits for DNS to answer or fail. With eight clients connecting concurrently and DNS down, the server completed 0.2 new connections per second. With log_hostname off and healthy DNS, those same eight clients completed 160 per second. These results describe that reported test, not a universal capacity limit.</p><h3>What Happens When Reverse DNS Fails?</h3><p>The following entries are from one of the reported 20-second connections, with %h in the prefix. Timestamps and process IDs are replaced by …; long entries are wrapped for readability.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*o9h2K0-oVd0xvWL9qzZrMw.png" /></figure><p>In that test, a failed lookup produced a WARNING and <strong>???</strong> in place of the client’s host and port. The IP address already known to the server was absent from the log. It remained available in <strong>pg_stat_activity.client_addr</strong> only for as long as the session stayed open. The final entry also identifies the delay: around 20 seconds of setup, including only 5.6 ms of authentication.</p><p>The cost did not depend on what the log displayed. Another run used the upstream default <strong>log_line_prefix</strong> with connection logging disabled, leaving no way for the log to print a hostname. Connections still waited 20 seconds. The only log evidence was one WARNING per connection.</p><h3>Why a Logged Hostname Is Not Proof of Identity</h3><p>When log_hostname is on, PostgreSQL records the PTR name it receives without checking that the name resolves back to the same IP. A PTR record belongs to the IP address rather than the domain named in it. Whoever manages reverse DNS for that address, usually the network owner or provider, sets it; the named domain’s owner does not control it. The party controlling the client’s reverse DNS can therefore choose the name shown in the log, including a name that resembles another IP address.</p><p>The source article checked this separately on PostgreSQL 18.6 using a controlled DNS server. It assigned the PTR value 10.0.0.5 to client address 127.0.0.5, and db-backup-01.corp.example, a name without an address, to 127.0.0.6. These five entries came from three connections, with <strong>%h</strong> at the end of the prefix and timestamps and process IDs replaced by … .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*n1H-ONFD3s2Q3TT5BrGjpQ.png" /></figure><p>Every prefix and the connection-received message used the PTR name. In this test, the real address appeared only inside a FATAL refusal message and in <strong>pg_stat_activity.client_addr</strong> while a session was open.</p><p>For access control, PostgreSQL does not accept that name without verification. As the PostgreSQL 18 documentation (<a href="https://www.postgresql.org/docs/18/auth-pg-hba-conf.html">PostgreSQL: Documentation: 18: 20.1. The pg_hba.conf File</a>) explains, a hostname rule in <strong>pg_hba.conf </strong>matches only if forward resolution also leads back to the client’s address. The final connection was rejected because the pg_hba.conf rule for db-backup-01.corp.example could not match a name with no address. The prefix displayed the name nonetheless.</p><p>During an investigation, a logged hostname is therefore a reverse-DNS answer, not proof of the client’s identity. With log_hostname off, <strong>%h</strong> records the connecting IP. If a name is needed later, resolve the address and check that the name resolves back to the same IP, as <strong>pg_hba.conf</strong> does and logging does not.</p><h3>Connection pools conceal the cost until reconnection</h3><p>Resolution happens once per connection, not per query. In one 30-second run per setting with eight persistent connections, the reported server handled 19,291 queries per second with log_hostname off and 19,165 with it on. One run per setting cannot establish whether the 0.7% difference is real. The mechanism explains why the effect should be small: an established connection performs no additional lookups.</p><p>A connection pool can consequently keep the cost out of view for a long time. It returns when the pool creates database connections, for example after a restart or failover. At those times, many pool connections may reopen together, and each performs its own lookup.</p><h3>How to check your server</h3><p>Connect using psql or your usual SQL client and run:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*S7bMqfFFBIME852Pufazyg.png" /></figure><p>🟣 If log_hostname is off, this setting performs no lookup and needs no change. To include the client address in the log, add <strong>%h</strong> to <strong>log_line_prefix</strong> as described below.</p><p>🟣 If it is on, check whether the names are used by <strong>%h</strong> or <strong>%r</strong> in the prefix, by connection logging, or by reports reading <strong>pg_stat_activity.client_hostname</strong>. If none uses them, each connection pays for a lookup without displaying its result. If they are used, compare the value of the names with the waiting time described above.</p><p>To disable it, run these commands in a superuser SQL session:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/716/1*wpmk0f2K8ckXVEWIYF81Gg.png" /></figure><p>This needs a configuration reload, not a restart, and affects connections opened afterward. To include the client address on each log line, add <strong>%h</strong> after <strong>%q</strong> in <strong>log_line_prefix</strong>. <strong>%q</strong> prints nothing; it tells background processes, which have no client, to stop reading the prefix at that point. The following superuser-session example shows the placement:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*eMOsjAu_x4X8sGMZy0X_JQ.png" /></figure><p>This is only an example of where to put <strong>%h</strong>, not a recommendation to replace the entire prefix. Retain the existing prefix, extend it with <strong>%h</strong>, and update the tools that consume your logs, including log analyzers and alert rules, for the changed format.</p><p>Open a new connection to confirm that its log entries show the IP address in the prefix. PostgreSQL 18 can also measure setup time on your server: adding <strong>setup_durations</strong> to <strong>log_connections</strong> produces a connection-ready entry with setup total for each new connection. That adds one line per connection, so on servers with frequent connections use it during a measurement window rather than permanently.</p><p>Managed services expose settings through their provider’s configuration system: parameter groups on Amazon RDS, server parameters on Azure Database for PostgreSQL, and database flags on Google Cloud SQL. The provider determines which settings can be changed. Use the query above to inspect the values actually running on the server.</p><h3>If you need host names</h3><p>Names can be useful, for example when client IPs change often and logs are retained for a long time. In that case, resolve addresses while reading the log instead of making clients wait during connection setup. A stored IP can be looked up during an investigation, but the answer describes DNS when the lookup is made, not necessarily when the connection occurred.</p><p>Two further points: behind a connection pooler, <strong>%h</strong> identifies the pooler’s address regardless of log_hostname. To identify the application associated with a log line, use <strong>application_name</strong>. The client supplies it, <strong>%a</strong> prints it, and no lookup is required.</p><h3>How the tests were performed</h3><p><em>The original experiment used two Azure VMs in Poland Central in a dedicated private network. The database host was Standard_D4s_v5 with Ubuntu 24.04, glibc 2.39, and PostgreSQL 18.6 from the PostgreSQL project’s packages; the client was Standard_D2s_v5. Settings remained as installed by the packages except for the prefix, connection logging, log-file handling, and listen address. Client figures came from pgbench 18.6. Server figures came from PostgreSQL 18 connection-ready setup-total messages for the benchmark’s own connections. For slow DNS, tc netem added 100 ms to every packet from the database host to Azure DNS. For DNS failure, iptables on the database host dropped Azure DNS answers: requests went out, but no answer returned. Each healthy-DNS variant ran three times in alternating order. The slow-DNS, failing-DNS, eight-client, and persistent-connection scenarios each ran once. The local DNS cache was cleared before every run. The unchecked-name experiment was separate: PostgreSQL 18.6 and dnsmasq ran on one Linux machine in an isolated network namespace, with the system resolver using DNS only and clients connecting from 127.0.0.5 and 127.0.0.6.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/1*OcbL2D3qmgXIRUuw3stk4A.png" /></figure><p><em>The cases from this article in one picture. Times are from our test: pgbench -C, no local DNS cache.</em></p><h3>In short</h3><p>🟣 %h in log_line_prefix records the client address without requiring a lookup.</p><p>🟣log_hostname changes the value from an IP address to a DNS name. It adds one lookup per new TCP connection, even when the name is never printed.</p><p>🟣In the reported tests, slow DNS delayed every new connection. When DNS did not answer, connections waited for the resolver to give up, and the log showed ??? instead of the address.</p><p>🟣 The logged name is not verified. Whoever controls reverse DNS for the client’s address chooses the PTR name.</p><p>🟣Keeping log_hostname off, its default, avoids this logging-related lookup. Resolve names while reading the logs if they are needed.</p><h3>Want a real say in what we build?</h3><p>Guardioz is in beta. You can sign up, and the product is early — we say so up front. Always free for one user. No credit card required. Every customer can influence what we build next: which problems matter most, which recommendations earn trust, and how the product develops.</p><p>➡️Start free: <a href="https://app.guardioz.com/register">https://app.guardioz.com/register</a></p><p>➡️See pricing: <a href="https://guardioz.com/pricing">https://guardioz.com/pricing</a></p><p>➡️ Questions first? Write to contact@guardioz.com.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ac6511a44b46" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why We’re Building Guardioz]]></title>
            <link>https://medium.com/@admin_7868/why-were-building-guardioz-98836d81d7f3?source=rss-bfdcfef1f708------2</link>
            <guid isPermaLink="false">https://medium.com/p/98836d81d7f3</guid>
            <category><![CDATA[database-administration]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[postgresql]]></category>
            <dc:creator><![CDATA[Guardioz]]></dc:creator>
            <pubDate>Thu, 22 Oct 2026 11:29:01 GMT</pubDate>
            <atom:updated>2026-10-28T18:48:36.467Z</atom:updated>
            <content:encoded><![CDATA[<p><em>PostgreSQL performance guidance for teams without a dedicated DBA</em></p><p>A PostgreSQL database slows down. Query durations change, resource use rises, or a dashboard starts showing a warning. The team has data to investigate — but where should it look first?</p><p>In many engineering teams, that question reaches a backend engineer, a DevOps specialist, a site reliability engineer, a platform engineer, or a founder. They run PostgreSQL in production, but database administration is one responsibility among many.</p><p>We started building Guardioz to help these teams move from noticing a database symptom to deciding what to investigate or do next.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*X0FfsSI0rUNr5Jttnth39w.jpeg" /></figure><h3><strong>The investigation can be harder than the fix.</strong></h3><p>When PostgreSQL behaves unexpectedly, finding the cause can take longer than applying the eventual fix. Root cause analysis may require engineers to correlate metrics, logs, query statistics, and configuration details across several tools and views.</p><p>Under pressure, scaling up can seem like the fastest way to relieve a symptom. Sometimes additional capacity is the right response. In other cases, it may reduce the visible impact without resolving the underlying cause — while increasing infrastructure costs.</p><p>We believe investigations should be more focused, and that teams should have a better chance of spotting issues before they turn into urgent production incidents.</p><h3><strong>PostgreSQL already provides signals. The challenge is interpreting them.</strong></h3><p>The PostgreSQL ecosystem offers a wealth of useful information. Tools such as Grafana and pgBadger, alongside PostgreSQL statistics such as those exposed through pg_stat_statements, help teams observe database behavior and investigate performance.</p><p>But signals need context.</p><p>Which finding matters for this workload? How serious is it? Is it a genuine issue or normal variation? What should the team examine first?</p><p>Answering those questions often depends on experience: knowing what healthy behavior looks like, understanding which configuration settings may be relevant, and connecting evidence from different sources. Raw data is necessary, but it does not always make the next step clear.</p><h3><strong>Not every team has a dedicated DBA.</strong></h3><p>Engineering teams can build and operate sophisticated systems without hiring a specialist for every technology. That can be a sensible choice, but it does not remove the need for database expertise.</p><p>In some organizations, that knowledge sits with one experienced engineer. In others, it comes from a consultant or internal documentation that may not keep up with a changing system. When an issue appears, the team still needs to interpret the evidence and decide what to do.</p><p>We want to make useful PostgreSQL guidance more accessible without assuming every team needs a full-time database administrator.</p><h3><strong>Small issues can add up.</strong></h3><p>Not every operational problem starts with an outage. It may be an index that is no longer useful, a configuration setting copied from an old example, or a default that no longer fits the workload.</p><p>PostgreSQL defaults are a starting point for unknown hardware and workloads. A setting that needs adjustment is not a judgment about the engineer who configured it.</p><p>Individually, these issues may not trigger an alert. Over time, they can contribute to slower queries, avoidable infrastructure costs, or incidents that are harder to investigate.</p><p>Prevention does not mean predicting every failure. It means making relevant signals easier to recognize while there is still time to investigate them calmly.</p><h3><strong>What Guardioz does today.</strong></h3><p>Guardioz turns PostgreSQL’s own signals into clear, prioritized findings, each with an explanation and a safe next step. Today, its checks cover server settings, indexes, autovacuum, and write-ahead log (WAL) archiving.</p><p>The aim is to answer three questions: what is wrong, how serious is it, and what should the team investigate or change first?</p><p>Recommendations show the measured data from your own server that they rely on and state whether a change requires a restart. Where available, they include SQL to review and run. Other findings call for a diagnostic check rather than a command.</p><p>Guardioz monitors your server around the clock, on a schedule. The agent collects a snapshot roughly once an hour.</p><p>It runs on the database server, connects to PostgreSQL locally, and executes a fixed set of queries in read-only sessions. It initiates the outbound connection to Guardioz over HTTPS, so you do not need to open an inbound port or expose the database port to the internet.</p><h3><strong>You review the evidence. You decide what to change.</strong></h3><p>Each finding includes a rationale that engineers can review. The intention is to provide evidence and recommendations that teams can evaluate, not a conclusion they must accept without an explanation.</p><p>Guardioz does not apply changes automatically. You decide whether and when to run a command.</p><p>Today, Guardioz supports PostgreSQL 18 on self-managed servers, with the agent installed on the primary, not a standby replica. Query analysis is planned; it is not a capability available today.</p><h3><strong>From monitoring to decisions.</strong></h3><p>Monitoring remains essential. Guardioz is not intended to replace monitoring tools or the engineers responsible for production systems. It helps teams interpret PostgreSQL signals, prioritize what matters, and decide what to investigate or change next.</p><p>We want engineers to spend less time bringing evidence together from different places and more time making informed decisions — then getting back to building.</p><h3><strong>Help decide what we build next.</strong></h3><p>Guardioz is in beta. You can sign up, and the product is early — we say so up front. Always free for one user. No credit card required.</p><p>Customers have a real say in what we build next, during the beta and after it: which problems matter most, which recommendations are useful, and where the explanations need to be clearer.</p><p>Learn more and sign up at <a href="https://guardioz.com">https://guardioz.com</a>.</p><p><em>The Guardioz Team</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=98836d81d7f3" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>