Skip to content

Ledger Nano S Setup Guide

Goal: Secure SSH authentication, GPG signing, and SOPS secret management using your Ledger Nano S, all integrated with your Nix flake.

Stack:

  • Hardware: Ledger Nano S
  • SSH/GPG: ledger-agent (from trezor-agent package)
  • Secrets: SOPS + GPG
  • Platform: macOS with Nix/nix-darwin


  • Ledger Nano S device
  • USB cable
  • macOS system with Nix installed
  • This Nix configuration cloned locally

The hardware security profile includes all required software. It’s already configured in your setup via the hardware-security.nix profile.

Nix packages (from nix/profiles/hardware-security.nix):

  • ledger-agent - Ledger SSH/GPG agent from trezor-agent
  • ledger-ssh-agent - Standalone SSH agent for Ledger
  • gnupg - GPG for encryption/signing
  • sops - Secrets management (from nix/modules/secrets/sops.nix)

Homebrew casks (from nix/modules/darwin/homebrew.nix):

  • ledger-live - GUI app for Ledger device management

If not already activated, apply your Nix configuration:

Terminal window
cd /Users/wikigen/Config
darwin-rebuild switch --flake .#wikigen-mac
Terminal window
# CLI tools (from Nix)
which ledger-agent
which ledger-gpg-agent
which sops
# GUI app (from Homebrew)
open -a "Ledger Live"

Status: ✅ Already configured in your Nix flake


Terminal window
open -a "Ledger Live"
  1. Connect your Ledger Nano S via USB
  2. Follow the on-screen setup:
    • Choose “Set up as new device”
    • CRITICAL: Write down your 24-word recovery phrase on paper
    • Store it in a safe place (NOT on your computer)
    • Set a PIN code (6-8 digits recommended)
    • Confirm your recovery phrase by entering words when prompted

Ledger Live will check for firmware updates. Install any available updates to ensure compatibility with latest apps.

Status: ✅ Complete when device is initialized


The SSH/PGP Agent app enables your Ledger to handle SSH authentication and GPG operations.

  1. Open Ledger Live
  2. Click the gear icon (⚙️) in the top right for Settings
  3. Navigate to “Experimental features” tab
  4. Toggle “Developer mode” to ON

This reveals developer apps including SSH/PGP Agent.

  1. Navigate to “Manager” (left sidebar)
  2. Connect and unlock your Ledger device (enter PIN)
  3. Search for “SSH/PGP Agent”
  4. Click “Install” button
  5. Confirm on your Ledger device (press both buttons)
  6. Wait for installation to complete

On your Ledger device:

  1. Scroll through apps
  2. You should see “SSH/PGP Agent”
  3. Open it (press both buttons)
  4. Screen should show “SSH/PGP Agent is ready”

Status: ✅ Complete when app is installed


Your Nix configuration automatically sets up GPG to use your Ledger for signing and encryption.

The hardware-security profile includes a launchd service that automatically starts ledger-gpg-agent on login. To start it manually:

Terminal window
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &

The Ledger SSH/PGP Agent app generates keys based on your device’s seed (24-word phrase). To get your GPG public key:

Terminal window
# Set GPG home directory
export GNUPGHOME=~/.gnupg-ledger
# Start agent if not running
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
sleep 2
# Get your public key
ledger-gpg-agent --homedir ~/.gnupg-ledger -v

The output will show your GPG key fingerprint. In this configuration, it’s:

D2A7EC63E350CC488197CB2ED369B07E00FB233E
Terminal window
# Export public key
gpg --homedir ~/.gnupg-ledger --export --armor > ~/ledger-gpg-public.asc
# Import to system keyring (optional, for verification)
gpg --import ~/ledger-gpg-public.asc
Terminal window
# Ensure Ledger is unlocked and SSH/GPG Agent app is open
echo "test" | gpg --homedir ~/.gnupg-ledger --clearsign

The Ledger will display “Sign message” - press the button to confirm.

Status: ✅ Configured via nix/profiles/hardware-security.nix


Your Nix configuration includes two SSH agent options:

Uses gpg-agent with SSH support (already configured):

Terminal window
# The hardware-security profile sets this automatically:
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

Get your SSH public key:

Terminal window
ssh-add -L

Uses ledger-agent directly:

Terminal window
# The launchd service starts this automatically
# Or run manually:
ledger-agent -d ssh://ledger@localhost

Get your SSH public key:

Terminal window
ssh-add -L
  1. Copy your SSH public key: ssh-add -L | pbcopy
  2. GitHub: Settings → SSH Keys → New SSH key
  3. Servers: Add to ~/.ssh/authorized_keys
Terminal window
# Test GitHub
ssh -T git@github.com
# The Ledger will prompt for confirmation - press button

Status: ✅ Configured via nix/profiles/hardware-security.nix


SOPS (Secrets OPerationS) uses your Ledger GPG key for encrypting/decrypting secrets.

Your configuration includes (from nix/modules/secrets/sops.nix):

sops = {
gnupg.home = "~/.gnupg-ledger";
defaultSopsFile = ./nix/secrets/secrets.yaml;
};
environment.variables = {
GNUPGHOME = "/Users/wikigen/.gnupg-ledger";
};

The .sops.yaml file specifies your GPG key for encryption:

creation_rules:
- path_regex: .*\.(yaml|json|env|ini)$
pgp: >-
D2A7EC63E350CC488197CB2ED369B07E00FB233E
Terminal window
# Set GPG home
export GNUPGHOME=~/.gnupg-ledger
# Ensure ledger-gpg-agent is running
pgrep -f ledger-gpg-agent || \
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
# Create/edit encrypted secret
sops nix/secrets/secrets.yaml

What happens:

  1. SOPS contacts Ledger via ledger-gpg-agent
  2. Ledger displays “Sign message”
  3. Press button to confirm
  4. Editor opens with decrypted content
  5. Make changes and save
  6. SOPS re-encrypts on save

In your host configuration:

{
# Declare a secret
sops.secrets."example/api_key" = {};
# Secret available at runtime:
# /run/secrets/example/api_key
}

Rebuild your system:

Terminal window
darwin-rebuild switch --flake .#wikigen-mac

Status: ✅ Configured via nix/modules/secrets/sops.nix


Terminal window
# Add to ~/.zshrc for persistent config
export GNUPGHOME=~/.gnupg-ledger
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
export GPG_TTY=$(tty)
Terminal window
# GPG
echo "test" | gpg --clearsign # Test GPG signing
gpg --list-keys # List GPG keys
# SSH
ssh-add -L # Show SSH public key
ssh -T git@github.com # Test GitHub SSH
# SOPS
sops nix/secrets/secrets.yaml # Edit secrets
sops -d nix/secrets/secrets.yaml # View secrets
sops -d --extract '["key"]' secrets.yaml # Extract specific value
# Agents
pgrep -f ledger-gpg-agent # Check GPG agent
pgrep -f ledger-agent # Check SSH agent
ledger-gpg-agent --homedir ~/.gnupg-ledger -v # Get GPG fingerprint
  • GPG keyring: ~/.gnupg-ledger/
  • GPG agent log: ~/.local/share/ledger-gpg-agent.log
  • SSH agent log: ~/.local/share/ledger-ssh-agent.log
  • SOPS config: .sops.yaml
  • Secrets: nix/secrets/
  • Decrypted secrets (runtime): /run/secrets/
  • GPG Fingerprint: D2A7EC63E350CC488197CB2ED369B07E00FB233E
  • Identity: Luc Chartier luc@distorted.media
  • Algorithm: ECDSA (NIST P-256)
  • Storage: Ledger Nano S hardware wallet

Checklist:

  • Ledger connected via USB
  • Ledger unlocked (PIN entered)
  • SSH/GPG Agent app is open on device
  • Screen shows “SSH/GPG Agent is ready”
  • Agent process is running

Test:

Terminal window
# Check agent is running
pgrep -f ledger-gpg-agent
# If not, start it
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
sleep 2
# Test GPG operation
echo "test" | gpg --homedir ~/.gnupg-ledger --clearsign

Solution:

Terminal window
# Ensure GNUPGHOME is set
export GNUPGHOME=~/.gnupg-ledger
# Verify key is visible
gpg --list-keys
# Should show: D2A7EC63E350CC488197CB2ED369B07E00FB233E

Solution:

Terminal window
# Check .sops.yaml has correct fingerprint
cat .sops.yaml
# Verify fingerprint matches
gpg --list-keys --fingerprint
# Ensure agent is running
pgrep -f ledger-gpg-agent || \
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &

Solution:

/Users/wikigen/.gnupg-ledger/S.gpg-agent.ssh
# Check SSH agent socket
echo $SSH_AUTH_SOCK
# Or set it manually:
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
# Verify key is loaded
ssh-add -L
# Test with verbose output
ssh -vT git@github.com

Solution:

Terminal window
# Kill existing agents
killall ledger-gpg-agent
killall ledger-agent
# Check Ledger is ready
# 1. Ledger unlocked
# 2. SSH/GPG Agent app open
# 3. Screen shows "ready"
# Restart agent
ledger-gpg-agent --homedir ~/.gnupg-ledger --server --verbose &
sleep 2
# Check logs
tail -f ~/.local/share/ledger-gpg-agent.error.log

Solution:

Terminal window
# Set editor in ~/.zshrc
export EDITOR="nano" # or "code --wait", "vim", etc.
# Or for single command
EDITOR=nano sops secrets.yaml

  • Private keys never leave Ledger: Only signs/decrypts on device
  • Physical confirmation required: Must press button for every operation
  • No remote exploitation: Can’t decrypt without physical device
  • Recovery: Keys derived from 24-word seed (keep secure!)

What Ledger protects against:

  • ✅ Key extraction from computer
  • ✅ Malware stealing keys from disk
  • ✅ Remote attacks on SSH/GPG keys
  • ✅ Unauthorized signing/decryption

What Ledger doesn’t protect against:

  • ❌ Malware intercepting decrypted data in memory
  • ❌ Physical theft of device (PIN provides basic protection)
  • ❌ Compromise of device if seed phrase is leaked
  • ❌ Side-channel attacks on device itself
  1. Lock screen when away from computer
  2. Remove Ledger when not actively using it
  3. Never share your 24-word recovery phrase
  4. Store seed phrase in secure physical location
  5. Use unique PIN - not shared with other devices
  6. Audit operations via Git history (SOPS commits)
  7. Rotate secrets if device is lost or compromised

Your keys are derived from your 24-word recovery phrase:

  1. Primary backup: Recovery phrase written on paper, stored securely
  2. Alternative access: Consider a second Ledger restored from same seed
  3. Test recovery: Periodically verify you can restore from seed
  4. SOPS multi-key: Add additional GPG keys to .sops.yaml for team access

Now that your Ledger is configured:

  • Add SSH public key to GitHub
  • Add SSH public key to servers
  • Test authentication: ssh -T git@github.com
  • Configure git signing (already done in hardware-security.nix)
  • Make a test commit to verify signing
  • Add GPG public key to GitHub for verified commits
  • Create first secret: sops nix/secrets/secrets.yaml
  • Use secret in config: sops.secrets."my-app/api-key" = {};
  • Rebuild system: darwin-rebuild switch --flake .#wikigen-mac
  • Verify secret: ls -l /run/secrets/
  • Set up multi-user SOPS access
  • Configure secret rotation procedures
  • Add secrets for cloud deployments
  • Integrate with CI/CD pipelines



Happy hardware security! 🔐