The Curious Case of the Duplicating GPG_TTY and the Hidden .zprofile Culprit (and Why My Terminal Was So Slow!)

Published: (January 14, 2026 at 10:07 AM EST)
5 min read
Source: Dev.to

Source: Dev.to

Cover image for The Curious Case of the Duplicating GPG_TTY and the Hidden .zprofile Culprit (and Why My Terminal Was So Slow!)

Image by joffi from Pixabay

Every now and then, a seemingly small configuration quirk can turn into a head‑scratching mystery, especially in the world of dotfiles. I recently found myself in such a predicament: the line

export GPG_TTY=$(tty)

kept magically reappearing in my .zshrc file every time I opened a new terminal. What started as a minor annoyance quickly became a fascinating dive into shell startup logic, Oh My Zsh plugins, and the often‑overlooked nuances of dotfile management—culminating in a significant performance hit to my terminal’s startup speed.

The Problem: A Frustratingly Sluggish Start (and a Massive Secret in .zshrc)

It began with a subtle, then increasingly painful, realization: my terminal was taking forever to start. What used to be an instantaneous pop‑up was now a sluggish crawl of several seconds. I’d open Cursor, Terminal.app, or Tabby, and just… wait.

After days of frustration I opened my .zshrc. To my absolute horror, it had grown to include thousands of identical lines:

export GPG_TTY=$(tty)

Every single one of those redundant lines was being parsed and executed by Zsh each time I opened a new terminal session, turning my snappy startup into a frustrating wait.

The GPG_TTY environment variable is crucial for GnuPG (GPG) to know which terminal to use for passphrase prompts—essential for signing Git commits. So, while the line itself was important, its relentless duplication (at least 3 000 times!) was definitely not.

Initial Suspicions: Oh My Zsh Plugins

My first thought, like many Zsh users, went straight to Oh My Zsh and its myriad of plugins. I knew there was a gpg-agent plugin, and its README.md explicitly stated:

“Updates the GPG_TTY environment variable before each shell execution.”

A quick grep confirmed the plugin contained the line export GPG_TTY=$TTY. This told me the plugin was indeed managing the variable. My initial theory was that the plugin might be flawed, constantly writing to my .zshrc. But plugins are usually sourced, not designed to write back to the user’s main configuration file. This was a critical distinction.

If the plugin was setting the variable, and I was also seeing it appended to .zshrc, it suggested a conflict: the plugin was doing its job, but something else was writing the redundant line.

The Hunt for the Writer

Knowing that the line was being written (not just sourced) was key. I systematically tried:

ActionResult
Remove the duplicate line from .zshrcIt reappeared
Disable the gpg-agent pluginThe line still reappeared
Comment out source $ZSH/oh-my-zsh.sh in .zshrc (disabling Oh My Zsh completely)The line still reappeared

This confirmed the culprit was outside of Oh My Zsh’s direct influence, meaning it was likely in another Zsh startup file or a system‑level script.

Zsh processes several startup files in a specific order:

  1. ~/.zshenv
  2. ~/.zprofile (login shells)
  3. ~/.zshrc (interactive shells)
  4. ~/.zlogin (login shells, after .zshrc)

Given the login‑shell behavior, .zprofile became a prime suspect.

The Aha! Moment: .zprofile Reveals Its Secrets

Opening my ~/.zprofile file, I immediately spotted it:

# https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key
# Add GPG key
if [ -r ~/.zshrc ]; then
    echo -e '\nexport GPG_TTY=$(tty)' >> ~/.zshrc
else
    echo -e '\nexport GPG_TTY=$(tty)' >> ~/.zprofile
fi

There it was—a script, seemingly taken directly from GitHub’s documentation on managing commit‑signature verification, designed to add export GPG_TTY=$(tty) to my .zshrc.

Here’s where I have to admit my own fault. Looking back at the documentation, it was clear that this command was meant to be run once as a terminal command to initialize the configuration. Instead, I had mistakenly copied the logic and pasted it directly into my ~/.zprofile.

The fatal flaw: the script lacked idempotence. It performed no check to see if GPG_TTY was already present in ~/.zshrc. Every time my login shell started and sourced .zprofile, it dutifully checked if .zshrc was readable (which it always was) and then unconditionally appended the line. Because .zprofile runs on every new login session, it was effectively “spamming” my .zshrc with new exports each time I opened my IDE or logged in.

The Fix and the Lesson Learned

  1. Delete the duplicates – I went back to my ~/.zshrc and manually removed all the thousands of duplicate export GPG_TTY=$(tty) lines.
  2. Remove the offending block – I deleted the entire if … else … fi block from my ~/.zprofile.

After that, opening a new terminal left my .zshrc pristine, GPG_TTY was still correctly set (thanks to the Oh My Zsh gpg-agent plugin), and most importantly, my terminal startup speed was back to being snappy!

Key Lessons Learned

  • Idempotence is king in dotfiles. Any script that modifies a configuration file should first check if the change is actually needed. Without this, you invite bloat, confusion, and significant performance degradation.
  • Understand your shell’s startup files. Knowing the order and purpose of .zshenv, .zprofile, .zshrc, .zlogin, etc., is vital for effective dotfile management.
  • Don’t copy‑paste whole command snippets into startup files. Documentation often shows a one‑off command; if you embed that logic into a file that runs repeatedly, you must adapt it to be safe for repeated execution.

With those take‑aways in mind, my terminal is fast again, and my dotfiles are clean—until the next mystery appears!

Troubleshooting

Third‑party documentation can be a double‑edged sword: while GitHub’s guide is intended to be helpful, the specific script provided for Zsh users could lead to issues if executed more than once, especially if it’s placed in a file like .zprofile that runs frequently.

Trust the specialized tools: If you’re using a plugin (like Oh My Zsh’s gpg-agent) designed to manage a specific environment variable, it’s often best to let it handle the heavy lifting.

This little adventure taught me a lot about the intricacies of my own shell environment. While it was a frustrating few days, the satisfaction of finally solving the mystery and reclaiming my fast terminal startup was well worth it. Keep your dotfiles clean, and always be wary of scripts that append without checking!

Back to Blog

Related posts

Read more »

Rapg: TUI-based Secret Manager

We've all been there. You join a new project, and the first thing you hear is: > 'Check the pinned message in Slack for the .env file.' Or you have several .env...

Technology is an Enabler, not a Saviour

Why clarity of thinking matters more than the tools you use Technology is often treated as a magic switch—flip it on, and everything improves. New software, pl...