chmod 600

rw-------

chmod 600 sets the mode to rw-------: the owner can read and write (6), and the group and everyone else get nothing (0). It’s the mode for secrets: SSH private keys, ~/.ssh/authorized_keys and ~/.ssh/config, .env files, TLS private keys, ~/.pgpass, anything holding a password or a token.

Why tools insist on it

OpenSSH ignores a private key that the group or others can access at all:

WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions 0644 for '/home/alice/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

600 and 400 pass; 640 and 644 don’t. ssh-keygen writes new private keys as 600 itself, so the error usually means a key that was copied, unzipped or checked out. PostgreSQL’s libpq likewise skips a ~/.pgpass with group or world access and asks for u=rw (0600) or less.

chmod 600 ~/.ssh/id_ed25519 ~/.ssh/authorized_keys ~/.ssh/config
chmod 700 ~/.ssh
stat -c '%a %n' ~/.ssh/id_ed25519   # 600 /home/alice/.ssh/id_ed25519

Create it private, don’t fix it after

chmod after writing leaves a window in which the secret is readable. A umask of 077 makes the file 600 from the moment it exists, since 666 with 077 cleared is 600:

(umask 077; printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_ed25519)

The parentheses keep the umask to that one command.

Gotchas

  • It’s not for directories. A 600 directory has no execute, so even its owner can’t open the files in it. Directories that hold secrets want 700.
  • Root still reads it. So does anyone with sudo. The mode keeps out other users, not administrators.
  • The service user must own it. A 600 file owned by root is unreadable to a service running as www-data. Either chown it to the service’s user, or keep root as owner and use 640 with the service’s group.

In git, CI and Kubernetes

  • Git can’t store 600: it records 100644, and the file comes back 644 on checkout. Secrets shouldn’t be in the repository anyway.
  • GitHub Actions: write a deploy key from a secret with the umask trick:
- name: Install the deploy key
  env:
    DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
  run: |
    mkdir -p ~/.ssh && chmod 700 ~/.ssh
    (umask 077; printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_ed25519)
  • Kubernetes mounts Secret files as 0644 unless told otherwise. Set defaultMode: 0600 on the volume, 384 in JSON, or mode on a single key under items.
  • Dockerfile: COPY --chmod=600 sets it, but a key copied into an image layer stays in that layer. Use a BuildKit secret mount (RUN --mount=type=secret,id=key) for anything the build only needs once.

Try it in the chmod Calculator

600 rw-------

WhoReadWriteExecute
Owner
Group
Others
Special bits
Command
chmod 600 file

Runs in your browser. Nothing you enter is sent anywhere.