<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux on UIPad Blog</title><link>https://blog.uipad.com/tags/linux/</link><description>Recent content in Linux on UIPad Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 26 Apr 2026 21:30:00 +0800</lastBuildDate><atom:link href="https://blog.uipad.com/tags/linux/index.xml" rel="self" type="application/rss+xml"/><item><title>Docker + Postgres Auth Voodoo: A Troubleshooting Log of a Fatal Nano Auto-Wrap Bug</title><link>https://blog.uipad.com/post/2026-04/docker-postgres-auth-nano-wrap-bug/</link><pubDate>Sun, 26 Apr 2026 21:30:00 +0800</pubDate><guid>https://blog.uipad.com/post/2026-04/docker-postgres-auth-nano-wrap-bug/</guid><description>&lt;p&gt;As an indie developer who constantly tinkers with servers, dealing with Docker is a daily routine. I thought deploying a PostgreSQL instance via &lt;code&gt;docker-compose up -d&lt;/code&gt; was a standard operation I could do with my eyes closed. However, I recently fell headfirst into a cascading series of traps.&lt;/p&gt;
&lt;p&gt;The error message is one we are all too familiar with:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;FATAL: password authentication failed for user &amp;quot;app&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But after repeatedly verifying that my environment variables, network configurations, and even inter-container connectivity were all flawless, things started getting weird. After two days of peeling back the layers, I discovered this wasn&amp;rsquo;t just a simple password typo. It was a triple-threat trap composed of &lt;strong&gt;phantom configurations, volume mounting blind spots, and editor quirks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This post logs the entire debugging process, hoping to save the sanity of anyone else doubting their existence in the terminal.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="trap-1-the-two-negatives-make-a-positive-phantom-password"&gt;Trap 1: The &amp;ldquo;Two Negatives Make a Positive&amp;rdquo; Phantom Password
&lt;/h3&gt;&lt;p&gt;The whole thing started like this: I noticed my newly created App container absolutely refused to connect to Postgres, yet another Auth service I had deployed earlier was connecting just fine.&lt;/p&gt;
&lt;p&gt;At first, I wondered if routing through the internal Docker network (e.g., &lt;code&gt;Host=pgsql&lt;/code&gt;) bypassed the password check. But Postgres&amp;rsquo;s &lt;code&gt;pg_hba.conf&lt;/code&gt; mechanism dictates that TCP connections must use &lt;code&gt;scram-sha-256&lt;/code&gt; validation; passwordless entry simply isn&amp;rsquo;t an option here.&lt;/p&gt;
&lt;p&gt;By directly running an &lt;code&gt;echo&lt;/code&gt; on the environment variables inside the container, I caught the first mole: &lt;strong&gt;unquoted &lt;code&gt;.env&lt;/code&gt; comments&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In an earlier iteration of the project, I had an &lt;code&gt;.env&lt;/code&gt; file written like this:
&lt;code&gt;PGSQL_APP_PASSWORD=app@pgsql # Global app password&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And my &lt;code&gt;docker-compose.yaml&lt;/code&gt; looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;POSTGRES_APP_PASSWORD&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${PGSQL_APP_PASSWORD:-app@pgsql}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because the &lt;code&gt;.env&lt;/code&gt; value wasn&amp;rsquo;t enclosed in double quotes, Docker Compose brutally swallowed the trailing spaces and the inline comment right into the environment variable.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;During database initialization, it saved &lt;code&gt;app@pgsql # Global app password&lt;/code&gt; as the complete, literal password.&lt;/li&gt;
&lt;li&gt;The old Auth container read that exact same configuration and made requests using that ridiculously long, comment-included password. &lt;strong&gt;Both sides matched perfectly (two negatives made a positive)!&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Meanwhile, when I manually typed the clean password &lt;code&gt;app@pgsql&lt;/code&gt; in the command line, I was ruthlessly rejected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; For complex strings in &lt;code&gt;.env&lt;/code&gt; files or &lt;code&gt;docker-compose.yml&lt;/code&gt;, especially passwords with special characters, &lt;strong&gt;always make it a habit to use double quotes&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="trap-2-the-ignored-volume-initialization-blind-spot"&gt;Trap 2: The Ignored Volume Initialization Blind Spot
&lt;/h3&gt;&lt;p&gt;Having discovered the issue above, I decided to fix the config, switch to a clean password &lt;code&gt;app#pgsql&lt;/code&gt;, and restart the container. The result? &lt;strong&gt;Still throwing the same error!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I went inside the container, printed the environment variable, and confirmed &lt;code&gt;$POSTGRES_APP_PASSWORD&lt;/code&gt; was now the correct &lt;code&gt;app#pgsql&lt;/code&gt;. Why couldn&amp;rsquo;t it connect?&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s when I noticed this line in my &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;./data:/var/lib/postgresql/data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This triggers a classic &amp;ldquo;hidden rule&amp;rdquo; of the Postgres Docker image: &lt;strong&gt;As long as the mounted target directory (&lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;) is not empty, the container startup will completely skip the initialization process (including the execution of scripts under &lt;code&gt;/docker-entrypoint-initdb.d/&lt;/code&gt;).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because I had already spun it up once, the &lt;code&gt;./data&lt;/code&gt; directory on my host machine contained legacy data. Even though I changed the config and restarted the container, the password stored inside the database was still that Chinese-comment-infused &amp;ldquo;phantom password&amp;rdquo; from the first initialization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; When debugging initialization scripts in a development environment, if you change passwords or the initial database structure, &lt;strong&gt;you must wipe the host&amp;rsquo;s &lt;code&gt;./data&lt;/code&gt; directory&lt;/strong&gt; (&lt;code&gt;rm -rf ./data/*&lt;/code&gt;), or drop into the container and forcefully update the password using &lt;code&gt;ALTER USER&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="final-boss-the-backstab-of-nanos-copy-paste"&gt;Final Boss: The &amp;ldquo;Backstab&amp;rdquo; of Nano&amp;rsquo;s Copy-Paste
&lt;/h3&gt;&lt;p&gt;Alright, I deleted the old data and was certain it would re-initialize this time. The &lt;code&gt;docker logs&lt;/code&gt; clearly showed my custom script &lt;code&gt;01-init-user-and-permissions.sh&lt;/code&gt; executing, even printing &lt;code&gt;NOTICE: User created: app&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I eagerly went to test the connection.
&lt;strong&gt;Error: &lt;code&gt;password authentication failed&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At that moment, I genuinely started to question if my fundamental understanding of Docker had been tampered with. The network was fine, the environment variables were fine, the data had been wiped clean—where was the ghost?&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t until I opened that &lt;code&gt;01-init-user-and-permissions.sh&lt;/code&gt; script and scanned it line by line that I finally spotted the incredibly stealthy fatal flaw:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CREATE USER &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$POSTGRES_APP_USER&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; WITH PASSWORD &lt;span style="color:#e6db74"&gt;&amp;#39;$POSTGRES_APP_PASSWO
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;RD&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, you read that right. &lt;strong&gt;The variable name &lt;code&gt;$POSTGRES_APP_PASSWORD&lt;/code&gt; had been chopped in half by a line break!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How did this happen?
Because I was working directly in an SSH terminal, using the &lt;code&gt;nano&lt;/code&gt; editor, and I &lt;strong&gt;copied and pasted&lt;/strong&gt; the script from another server.
When your terminal window isn&amp;rsquo;t wide enough, and &lt;code&gt;nano&lt;/code&gt; has its default &lt;strong&gt;Word Wrap&lt;/strong&gt; feature enabled, it takes the liberty of inserting an actual &lt;strong&gt;Hard Return&lt;/strong&gt; right in the middle of long strings.&lt;/p&gt;
&lt;p&gt;In Bash logic, it couldn&amp;rsquo;t find the variable &lt;code&gt;$POSTGRES_APP_PASSWO&lt;/code&gt; (because it was truncated by the return key), so it parsed it as an &lt;strong&gt;empty string&lt;/strong&gt;.
Ultimately, the database successfully executed the SQL, but what it actually ran was:
&lt;code&gt;CREATE USER &amp;quot;app&amp;quot; WITH PASSWORD '';&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The password was blank!&lt;/strong&gt; This was the ultimate reason why my perfectly correct password was constantly failing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="summary--best-practices"&gt;Summary &amp;amp; Best Practices
&lt;/h3&gt;&lt;p&gt;Stacking these three pitfalls together creates maximum dramatic effect. To prevent myself (or you, the reader) from falling into this trap again, here are a few ironclad rules for development:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminal Editor Self-Defense:&lt;/strong&gt;
If you need to paste long code or configs into &lt;code&gt;nano&lt;/code&gt; on a Linux terminal, &lt;strong&gt;always remember to add the &lt;code&gt;-w&lt;/code&gt; flag&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nano -w script.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This disables auto word wrapping and is an absolute lifesaver. An even better approach is using VS Code&amp;rsquo;s Remote SSH extension to edit server files directly, bypassing terminal clipboard torture entirely.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Environment Variable &amp;amp; Password Standards:&lt;/strong&gt;
Wrap &lt;code&gt;.env&lt;/code&gt; passwords and variables containing special characters in double quotes. Keep inline comments on their own separate lines.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Global app password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PGSQL_APP_PASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;app@pgsql&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The 3-Step DB Debugging Axis:&lt;/strong&gt;
When encountering Docker DB password voodoo, drop straight into the host with superadmin privileges to investigate and settle it instantly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1. Check what configuration the container actually swallowed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec -it pgsql /bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo $POSTGRES_APP_PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2. Force a connection test using the exact environment variable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PGPASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$POSTGRES_APP_PASSWORD&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; psql -h 127.0.0.1 -U app -d postgres
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Server operations are just like this. Sometimes, what tortures you for two straight days isn&amp;rsquo;t some low-level kernel bug, but simply an invisible line break. Documenting this down, mostly as a stark reminder to my future self!&lt;/p&gt;</description></item></channel></rss>