# A deep dive into Ghostty

> Ghostty from scratch: install it, learn the shortcuts, write the config, pick fonts and themes, use splits, build keybindings, set up shell integration and SSH.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-09-10 | Topics: [CLI](https://flaviocopes.com/tags/cli/) | Canonical: https://flaviocopes.com/ghostty/

Ghostty is a terminal emulator for macOS, Linux, and FreeBSD. It's fast, it uses native platform UI, and it supports the modern terminal protocols that programs like Neovim, Claude Code, and Yazi use today.

It also works with zero configuration. You install it, you open it, you have a good terminal.

Most of this guide is about what happens after that, when you want to make it yours: the config file, fonts, themes, splits, keybindings, shell integration, SSH. But a terminal emulator is only the window. The real skill is using the terminal, and Ghostty is a good way to do that. So before the configuration part I put the things that matter first: the shortcuts you'll use every day, how I use Ghostty, and where to learn the terminal itself, with links to my courses, books, and guides.

I wrote this guide against Ghostty 1.3.1, the stable release at the time of writing.

## What is Ghostty?

Ghostty was created by Mitchell Hashimoto ([https://x.com/mitchellh](https://x.com/mitchellh)), a co-founder of HashiCorp. He started it as a side project, ran a private beta for a couple of years, and released 1.0 in December 2024.

The code is MIT licensed. During the 1.3 cycle, Ghostty became a fiscally sponsored non-profit project through Hack Club.

The interesting part of the architecture is the split between the core and the apps. The core is a library written in Zig called **libghostty**. 

It does terminal emulation, font handling, and GPU rendering. 

The macOS app is written in Swift with AppKit and SwiftUI. The Linux app is written in Zig on top of GTK4 and libadwaita. Both use libghostty underneath.

That split is what makes Ghostty feel native on both platforms. Tabs are real macOS tabs. Windows behave like GTK windows on Linux, and keyboard shortcuts follow the conventions of each system, so on a Mac you use Cmd and on Linux you use Ctrl+Shift.

The library also lets other people build terminals on top of it. [cmux](https://flaviocopes.com/cmux/), for example, uses libghostty for its terminal panes. And it's not alone: [awesome-libghostty](https://github.com/Uzaaft/awesome-libghostty) tracks projects built on the full library and projects using only `libghostty-vt`, its terminal parser and state layer. The list includes bindings for Go, Rust, Swift, Node, Python, and .NET. There are terminals for iOS, Android, Windows, Emacs, and even the Apple Watch. [ghostty-web](https://github.com/coder/ghostty-web) provides a web version with an xterm.js-compatible API. OrbStack uses Ghostty technology for its built-in terminal. There is also a large category of macOS apps for running several coding agents side by side.

In 1.3.1, the standalone libghostty API was still unversioned and a work in progress. Check the exact version before building an app around it.

There is no Windows version yet. It's on the long term roadmap, but not planned for the near future.

## Ghostty compared to the alternatives

You probably already have a terminal you like. Here is how Ghostty fits next to the common ones.

Terminal.app on macOS is fine. Ghostty's own benchmarks show it falling behind with heavy output. Terminal.app has split panes, but both panes show the same session instead of opening independent shells. It also lacks features such as ligatures and the Kitty graphics protocol.

iTerm2 has a broad feature set, but it's macOS only. Ghostty covers the features I use most from iTerm2: splits, tabs, search, and a quick terminal.

Alacritty is fast and minimal. It intentionally leaves managed tabs and splits to your window manager or a terminal multiplexer such as tmux. Ghostty includes those windowing features.

Kitty is also fast and feature rich, but it draws its own UI instead of using the native one, and its keyboard-first design takes some learning. Its author designed the Kitty graphics and keyboard protocols, which Ghostty supports too.

WezTerm is cross-platform, including Windows, and you configure it in Lua. Ghostty's config is a plain text file with no programming language.

What sets Ghostty apart for me is the native UI combined with defaults good enough that many people never write a config file.

## Install Ghostty

Everything starts at [ghostty.org](https://ghostty.org), which has the download for macOS and the install instructions for Linux.

![The ghostty.org homepage, with the ASCII ghost logo and the Download and Documentation buttons](https://flaviocopes.com/images/ghostty/website.png)

### macOS

Download the `.dmg` from [ghostty.org/download](https://ghostty.org/download) and drag the app into Applications. Or use Homebrew:

```bash
brew install --cask ghostty
```

The cask repackages the same signed and notarized binary, so both methods give you the same app.

Ghostty needs macOS 13 or newer. 1.3 is the last release that supports macOS 13. Starting with 1.4 you need macOS 14.

### Linux

The Ghostty project only ships official binaries for macOS. Linux packages come from distribution maintainers, community maintainers, and the Ghostty project itself, depending on the package. The main ones:

```bash
# Arch Linux
sudo pacman -S ghostty

# Ubuntu 26.04 and newer
sudo apt install ghostty

# Omarchy
omarchy pkg add ghostty

# Fedora (community COPR)
sudo dnf copr enable scottames/ghostty
sudo dnf install ghostty

# Snap
sudo snap install ghostty --classic
```

Nix, Void, Gentoo, Solus, Alpine, and an AppImage are also covered on the [install page](https://ghostty.org/docs/install/binary).

### Check the version

Open Ghostty and run:

```bash
ghostty +version
```

You get the version plus some build details:

```text
Ghostty 1.3.1

Version
  - version: 1.3.1
  - channel: stable
Build Config
  - Zig version   : 0.15.2
  - build mode    : .ReleaseFast
  - app runtime   : .none
  - font engine   : .coretext
  - renderer      : renderer.generic.Renderer(renderer.Metal)
  - libxev        : kqueue
```

![Output of ghostty +version in a Ghostty window: version 1.3.1, stable channel, Zig 0.15.2, CoreText font engine, Metal renderer](https://flaviocopes.com/images/ghostty/version.png)

On macOS this works inside Ghostty because its shell integration adds the app's binary folder to your `PATH`. From another terminal, like the one inside your editor, you may need the full path: `/Applications/Ghostty.app/Contents/MacOS/ghostty`.

## The +actions

That `+version` is one of Ghostty's CLI actions. The `ghostty` binary is both the terminal and a small toolbox. Run `ghostty +help` to list them:

```text
  +version
  +help
  +list-fonts
  +list-keybinds
  +list-themes
  +list-colors
  +list-actions
  +ssh-cache
  +edit-config
  +show-config
  +validate-config
  +show-face
  +crash-report
  +boo
  +new-window
```

![Output of ghostty +help: usage, the note that on macOS the CLI only runs actions, and the list of available +actions](https://flaviocopes.com/images/ghostty/help.png)

We'll use most of these in the rest of the guide. `+boo` shows the Ghostty animation, so you can skip that one. `+new-window` only works on Linux.

Each action has its own help: `ghostty +list-themes --help`.

## What you get with no config

Before you write a single line of configuration, this is what a fresh Ghostty gives you.

The font is JetBrains Mono, which Ghostty embeds so it works even if you don't have it installed. The Nerd Fonts symbols are built in too, so prompts like Starship and tools like eza show their icons right away, without installing a patched font.

Shell integration is injected automatically for bash, zsh, fish, and nushell. Ghostty makes its Elvish module available, but you must load it yourself. New tabs open in the current directory, the cursor becomes a bar at the prompt, and closing a tab at an idle prompt doesn't ask for confirmation.

Tabs, splits, a command palette, search in the scrollback, and native scrollbars are all there with default shortcuts. Cmd+D splits right, Cmd+T opens a tab, Cmd+F searches.

The `TERM` variable is `xterm-ghostty`, and macOS Secure Input turns on by itself when you type a password.

My advice is to use it for a day with no config, and then come back here for the parts you miss.

## Default shortcuts

These are the defaults you'll use every day, on both platforms. They work with no config, so learn them before you change anything. `ghostty +list-keybinds --default` prints the complete list for your version.

| Action                         | macOS               | Linux                     |
| ------------------------------ | ------------------- | ------------------------- |
| New window                     | Cmd+N               | Ctrl+Shift+N              |
| New tab                        | Cmd+T               | Ctrl+Shift+T              |
| Close current split or tab     | Cmd+W               |                           |
| Close tab and all its splits   | Cmd+Option+W        | Ctrl+Shift+W              |
| Close window                   | Cmd+Shift+W         | Alt+F4                    |
| Quit                           | Cmd+Q               | Ctrl+Shift+Q              |
| Next / previous tab            | Cmd+Shift+] / [     | Ctrl+Page Down / Up       |
| Go to tab 1 to 8               | Cmd+1 to 8          | Alt+1 to 8                |
| Last tab                       | Cmd+9               | Alt+9                     |
| Split right                    | Cmd+D               | Ctrl+Shift+O              |
| Split down                     | Cmd+Shift+D         | Ctrl+Shift+E              |
| Focus split by direction       | Cmd+Option+Arrow    | Ctrl+Alt+Arrow            |
| Next / previous split          | Cmd+] / [           | Ctrl+Super+] / [          |
| Resize split                   | Cmd+Ctrl+Arrow      | Ctrl+Shift+Super+Arrow    |
| Equalize splits                | Cmd+Ctrl+=          |                           |
| Zoom split                     | Cmd+Shift+Enter     | Ctrl+Shift+Enter          |
| Fullscreen                     | Cmd+Enter           | Ctrl+Enter                |
| Copy                           | Cmd+C               | Ctrl+Shift+C              |
| Paste                          | Cmd+V               | Ctrl+Shift+V              |
| Select all                     | Cmd+A               | Ctrl+Shift+A              |
| Search                         | Cmd+F               | Ctrl+Shift+F              |
| Next / previous match          | Cmd+G / Cmd+Shift+G |                           |
| Jump to previous / next prompt | Cmd+Up / Down       | Ctrl+Shift+Page Up / Down |
| Scroll to top / bottom         | Cmd+Home / End      | Shift+Home / End          |
| Scroll a page                  | Cmd+Page Up / Down  | Shift+Page Up / Down      |
| Clear screen and scrollback    | Cmd+K               |                           |
| Font bigger / smaller / reset  | Cmd+= / - / 0       | Ctrl+= / - / 0            |
| Command palette                | Cmd+Shift+P         | Ctrl+Shift+P              |
| Open config                    | Cmd+,               | Ctrl+,                    |
| Reload config                  | Cmd+Shift+,         | Ctrl+Shift+,              |
| Inspector                      | Cmd+Option+I        | Ctrl+Shift+I              |
| Undo close                     | Cmd+Z               |                           |
| Screen to file, paste path     | Cmd+Shift+J         | Ctrl+Shift+J              |

The blank Linux cells are actions with no default binding there. You can bind them yourself, and the keybindings section explains how.

## How I use Ghostty

My config is short. This is all of it:

```ini
font-size = 18
macos-option-as-alt = false
keybind = super+bracket_right=increase_font_size:1
```

The font is the default JetBrains Mono, the theme is the default. I bumped the size to 18, which is big, and I like it that way.

`macos-option-as-alt = false` keeps the Option key as macOS intends it. I type on an Italian keyboard, and I want Option to keep producing the characters it normally does.

The extra keybind is about that same keyboard. The `+` key on an Italian Mac keyboard sits where a US keyboard has `]`, so I bound the physical `bracket_right` key to increase the font size. Cmd and the plus key do what I expect, whatever the layout thinks that key is called.

I also ended up with two config files, one in `~/.config/ghostty` and the template Ghostty created in `~/Library/Application Support`. Both are loaded, the second wins. That's a leftover from setting it up before I knew how the lookup works, and the XDG one is the one worth keeping.

My shell is fish, so shell integration is automatic. Cmd+Up between prompts, new splits in the current folder, and the bar cursor at the prompt all worked without any setup.

Most of my terminal time is coding agents, `npm run dev`, git, and SSH into my servers. Ghostty's job is to stay out of the way while that happens.

## Learn the terminal, not just Ghostty

Everything after this section is about Ghostty itself: fonts, themes, splits, keybindings, shell integration. It's the longest part of the guide, because that's where the questions are.

But none of it makes you better at the terminal. Ghostty runs a shell, the shell runs commands, and that's where the work happens. If the commands are new to you, no amount of config will help. If you know them well, you'll be fine in any terminal, and Ghostty is just a nicer one.

I've written about the terminal for years, and all of it is free, so here is the map.

**Start with the commands.** [The Command Line Guide](https://flaviocopes.com/cli/) is the overview. If you've never opened a terminal, start with [The Command Line for Complete Beginners](https://flaviocopes.com/cli-for-beginners/) instead. The free [Shell Commands course](https://flaviocopes.com/courses/terminal/) goes through the commands one lesson at a time, and the [CLI Handbook](https://flaviocopes.com/ebooks/cli-commands-handbook/) is the same material as a PDF or EPUB you can keep around. I also have a short post for most individual commands, like [ls](https://flaviocopes.com/linux-command-ls/), [cd](https://flaviocopes.com/linux-command-cd/), [grep](https://flaviocopes.com/linux-command-grep/), [find](https://flaviocopes.com/linux-command-find/), [tail](https://flaviocopes.com/linux-command-tail/), [chmod](https://flaviocopes.com/linux-command-chmod/), [ps](https://flaviocopes.com/linux-command-ps/), [kill](https://flaviocopes.com/linux-command-kill/), and [tar](https://flaviocopes.com/linux-command-tar/). The full list is under the [CLI tag](https://flaviocopes.com/tags/cli/).

**Then the shell.** The [Unix Shells Tutorial](https://flaviocopes.com/shells/) explains what a shell is and how bash, zsh, and fish differ. I use fish, and [The Fish Shell: A Complete Guide](https://flaviocopes.com/fish-shell/) is how I'd set it up today. If you'd rather stay with bash, read [The Bash shell](https://flaviocopes.com/bash/) and [Introduction to Bash Shell Scripting](https://flaviocopes.com/bash-scripting/), then the [Shell Scripting and Automation course](https://flaviocopes.com/courses/shell-scripting/). Two small ones you'll need on day one: [how to set an alias](https://flaviocopes.com/how-to-set-alias-shell/) and [how to set environment variables](https://flaviocopes.com/shell-environment-variables/).

**Tools that live in the terminal.** [tmux](https://flaviocopes.com/tmux/) for sessions that survive, and I compare it with Ghostty's own splits later in this guide. [cmux](https://flaviocopes.com/cmux/) if you run several coding agents at once. [zoxide](https://flaviocopes.com/zoxide/) to stop typing long `cd` paths. [jq](https://flaviocopes.com/jq-command/) for the JSON that every API and every agent log throws at you.

**Servers.** The terminal is how you talk to a remote machine. [SSH for developers](https://flaviocopes.com/ssh-for-developers/) covers keys, config, and tunnels, and the SSH section of this guide covers the Ghostty-specific part. To go further, the free [Linux Basics](https://flaviocopes.com/courses/linux-basics/), [VPS](https://flaviocopes.com/courses/vps/), [Linux Server Troubleshooting](https://flaviocopes.com/courses/linux-troubleshooting/), and [Build a Home Server](https://flaviocopes.com/courses/home-server/) courses take you from a fresh Ubuntu install to a machine you can run things on.

**Your Mac or Linux box.** The [macOS for Developers course](https://flaviocopes.com/courses/macos-for-developers/) covers the setup around the terminal: Homebrew, dotfiles, the tools worth installing. [A practical guide to Homebrew](https://flaviocopes.com/homebrew/) is the standalone version of the Homebrew part.

**Git.** You'll use it from the terminal more than from any GUI. The free [Git course](https://flaviocopes.com/courses/git/) and the [Git Cheat Sheet](https://flaviocopes.com/ebooks/git-cheat-sheet/) cover it.

The books and courses are all on the [free library page](https://flaviocopes.com/access/). Now, the config.

## The configuration file

Ghostty reads a plain text file, not Lua or JSON.

### Where the file lives

On both macOS and Linux, the preferred XDG location is:

```text
~/.config/ghostty/config.ghostty
```

The `.ghostty` extension arrived in 1.3 and helps editors pick the right syntax highlighting. The older file named `config` still works. If both files exist, Ghostty loads `config` first and `config.ghostty` second.

On macOS there is a second location that Ghostty also reads:

```text
~/Library/Application Support/com.mitchellh.ghostty/config.ghostty
```

The older extensionless name works there too. Ghostty loads the XDG files first and the Application Support files second, so values in Application Support win. It creates a commented template there on first launch if it finds no config anywhere.

I'd pick one and stick to it. `~/.config/ghostty/config.ghostty` also works on Linux, so that's the one I'd keep in a dotfiles repo.

### The syntax

Every line is `key = value`. Spaces around the `=` don't matter. Lines starting with `#` are comments. Blank lines are ignored.

```ini
# Fonts
font-family = JetBrains Mono
font-size = 14

# Colors
theme = Catppuccin Mocha

# Behavior
copy-on-select = clipboard
```

A few rules that trip people up:

Comments can't go at the end of a line. Ghostty treats everything after `=` as the value, so `background = #000 # black` tries to parse the whole string as a color and fails validation.

An empty value resets the key to its default:

```ini
font-family =
```

Some keys can appear more than once. `font-family` and `config-file` keep every value. `keybind`, `palette`, and `env` keep different triggers, palette indexes, or variable names, but a later duplicate replaces the earlier one:

```ini
font-family = Iosevka
font-family = JetBrains Mono
```

This sets Iosevka as the main font and JetBrains Mono as the fallback for glyphs Iosevka lacks. To start over in a repeatable key, set it to an empty string first: `font-family = ""`.

Values with special characters can be quoted: `font-family = "Fira Code"`.

### Reload the config

Press Cmd+Shift+, on macOS or Ctrl+Shift+, on Linux. The config reloads without restarting.

Not every option reloads. The docs for each option say if it applies at runtime, only to new terminals, or needs a full restart. Font size applies immediately. Padding applies to new windows, tabs, and splits. `background-opacity` on macOS needs a restart.

Cmd+, (or Ctrl+,) opens the config file in your default editor.

### Validate the config

Typos in a config file are silent in many programs. Ghostty has a checker:

```bash
ghostty +validate-config
```

No output and exit code 0 means the file is fine. With a mistake, you get the file, line, and problem:

```text
/Users/flavio/.config/ghostty/config:2:backgroun: unknown field
```

Ghostty also shows an error window at startup if the config has problems, but the command is what you want in a script or before a commit.

### See the effective config

```bash
ghostty +show-config
```

This prints what Ghostty is using after merging every file. Add `--default` to see every option with its default value, and `--docs` to include the documentation comments:

```bash
ghostty +show-config --default --docs | less
```

That's the complete reference, offline, for the exact version you're running. Over 4,000 lines. When the website and your installed version disagree, this output is right for your machine.

### Split the config into files

`config-file` includes another file. Paths are relative to the file that contains the directive:

```ini
config-file = fonts
config-file = keybinds
config-file = ?local
```

The `?` prefix makes the file optional. If `local` doesn't exist, nothing happens. This is how you keep a shared config in a dotfiles repo and a machine-specific file that stays out of git.

One thing to know: included files are processed after the whole current file. So a value set in `fonts` overrides the same key set later in the main file, not the other way around.

### Every option is a CLI flag

Any config key works as a command line flag when starting Ghostty:

```bash
ghostty --font-size=20 --theme="Rose Pine"
```

On macOS you can't launch the app that way from a shell. Use `open`:

```bash
open -na Ghostty.app --args --font-size=20
```

The `-n` opens a new instance and `-a` names the app. Flags after `--args` go to Ghostty.

## Fonts

### Pick a family

List what Ghostty can see:

```bash
ghostty +list-fonts
```

You get every family with its styles, one block per family:

![Output of ghostty +list-fonts, listing font families like Andale Mono, Berkeley Mono, DM Mono, and Menlo with their styles](https://flaviocopes.com/images/ghostty/list-fonts.png)

Then set it:

```ini
font-family = Iosevka
```

Bold, italic, and bold italic are found automatically from the same family. If you want a different font for one of those styles, there are dedicated keys:

```ini
font-family-bold = Iosevka Heavy
font-family-italic = Victor Mono
```

When a font has no italic, Ghostty synthesizes one by slanting the regular glyphs. Same for bold, by thickening the outline. If you'd rather see the regular style than a fake one:

```ini
font-synthetic-style = no-italic
```

### Fallbacks

Repeat `font-family` for fallbacks. The first entry is the main font, the rest are consulted in order when a character is missing:

```ini
font-family = Iosevka
font-family = Noto Sans Mono CJK JP
```

Emoji don't need a fallback. Ghostty always uses Apple Color Emoji on macOS and Noto Emoji on Linux unless you override it.

To find out which font renders a specific character:

```bash
ghostty +show-face --string="→ λ ⚡"
```

```text
U+2192 « → » found in face "JetBrains Mono".
U+3BB « λ » found in face "JetBrains Mono".
U+26A1 « ⚡ » found in face "Apple Color Emoji".
```

This is the command to run when an icon in your prompt shows up as a box.

### Size

```ini
font-size = 14
```

The unit is points, and decimals are allowed. On a retina display where 1pt is 2px, `13.5` gives you 27px, an odd pixel size you can't reach with whole numbers.

Cmd+= and Cmd+- change the size in the current terminal. Cmd+0 resets it. On Linux it's Ctrl instead of Cmd. New windows inherit the size of the focused one because `window-inherit-font-size` defaults to true.

### Ligatures

Fonts like Fira Code and JetBrains Mono turn `!=` and `=>` into single glyphs. Some people love it, some hate it. To turn them off:

```ini
font-feature = -calt
```

`calt` is the OpenType feature most coding fonts use for ligatures. Some fonts also need `-liga` and `-dlig`. You can list several: `font-feature = -calt, -liga, -dlig`.

The same key enables stylistic sets. Iosevka and Fira Code ship alternate glyphs behind `ss01`, `ss02`, and so on:

```ini
font-feature = ss01
font-feature = zero
```

Ghostty breaks ligatures under the cursor by default, so you can see individual characters while editing. `font-shaping-break = no-cursor` turns that off.

### Variable fonts

If your font is a variable font, you can set axes directly:

```ini
font-variation = wght=450
font-variation-bold = wght=700
```

Values outside the font's range are ignored, not clamped.

### Thicker text on macOS

macOS renders text thinner than Linux. If your font looks anemic:

```ini
font-thicken = true
```

`font-thicken-strength` goes from 0 to 255 and defaults to the max.

### Fix metrics

Every `adjust-*` key nudges a measurement the font declares. Values are pixels or percentages, and they are relative changes:

```ini
adjust-cell-height = 10%
adjust-underline-position = 2
adjust-cursor-thickness = 1
```

`adjust-cell-height` is the one people use most. It adds line spacing without changing the font. Text stays vertically centered in the taller cell.

### Map characters to a font

Sometimes one font has a better version of a specific symbol. `font-codepoint-map` forces a Unicode range to a font:

```ini
font-codepoint-map = U+E0B0-U+E0D4=Symbols Nerd Font Mono
```

This applies to new terminals only.

## Themes and colors

### Browse the built-in themes

Ghostty ships with over 450 themes. Run this inside Ghostty:

```bash
ghostty +list-themes
```

You get an interactive preview. Arrow keys move through the list, the right side shows the theme applied to sample text, F1 shows the other keys, and Escape exits.

Piped or with `--plain`, you get one theme per line, followed by its source marker:

```bash
ghostty +list-themes --plain | grep -i gruvbox
```

`ghostty +list-themes --help` explains the preview, the two folders it searches, and the `--color` flag that limits the list to dark or light themes:

![Output of ghostty +list-themes --help, describing the TUI preview, the two theme directories, and the --path, --plain, and --color flags](https://flaviocopes.com/images/ghostty/list-themes-help.png)

### Set a theme

```ini
theme = Catppuccin Mocha
```

Popular ones you'll find in the list: `Catppuccin Latte`, `Catppuccin Mocha`, `Dracula`, `GitHub Dark`, `Gruvbox Dark`, `Nord`, `One Half Dark`, `Rose Pine`, `TokyoNight`, `Solarized Dark Higher Contrast`. Use the theme name exactly, without the `(resources)` or `(user)` marker.

### Light and dark mode

Ghostty follows the system appearance if you give it two themes:

```ini
theme = light:Catppuccin Latte,dark:Catppuccin Mocha
```

Switch macOS to light mode and the terminal changes with it. Programs inside the terminal that support the color scheme notification (Neovim does) can switch their own theme at the same time.

### Write your own theme

A theme file uses the regular Ghostty config syntax and normally sets colors. Put it in `~/.config/ghostty/themes/` and refer to it by file name.

Here is a small light theme in the colors of this site:

```ini
# ~/.config/ghostty/themes/newspaper
background = #f5f0e6
foreground = #101214
cursor-color = #ff4b0a
selection-background = #101214
selection-foreground = #f5f0e6
palette = 0=#101214
palette = 1=#e92820
palette = 2=#4a6b2a
palette = 3=#b8730a
palette = 4=#1f4b7a
palette = 5=#6b3a7a
palette = 6=#2a6b6b
palette = 7=#f5f0e6
palette = 8=#5a5a5a
palette = 9=#ff4b0a
palette = 10=#6b8f3a
palette = 11=#d69a1a
palette = 12=#2f6da8
palette = 13=#8f5aa8
palette = 14=#3a8f8f
palette = 15=#ffffff
```

Then:

```ini
theme = newspaper
```

Theme files can set almost any option, not only colors. `theme` and `config-file` are silently ignored inside a theme file. Don't load themes from sources you don't trust. The built-in ones are audited.

### Override single colors

Anything you set in the main config wins over the theme:

```ini
theme = Nord
background = #1a1d24
cursor-color = #ff4b0a
```

`palette` sets one of the 256 colors. The first 16 are the ANSI colors programs use most:

```ini
palette = 1=#e92820
palette = 9=#ff4b0a
```

Colors accept `#RRGGBB`, `RRGGBB`, or X11 names like `orange`. `ghostty +list-colors` prints the names.

### Cursor

```ini
cursor-style = bar
cursor-style-blink = false
cursor-color = #ff4b0a
cursor-text = #ffffff
```

Styles are `block`, `bar`, `underline`, and `block_hollow`. Shell integration switches the cursor to a bar at the prompt regardless of this setting. If you want your style everywhere, add `shell-integration-features = no-cursor`.

`cursor-color` and `cursor-text` can be `cell-foreground` or `cell-background` to follow the text under the cursor.

### Selection colors

```ini
selection-background = #585b70
selection-foreground = #cdd6f4
```

Without these, selection inverts the window colors. `selection-foreground = cell-foreground` keeps syntax colors visible inside a selection.

### Make text readable

Some themes let a program print dark blue on black. `minimum-contrast` forces a minimum ratio between text and background:

```ini
minimum-contrast = 3
```

The value is a WCAG contrast ratio from 1 to 21. Around 1.1 prevents invisible text. Around 3 makes everything readable. Higher values push more text toward pure black or white.

### Bold and faint text

```ini
bold-color = bright
faint-opacity = 0.6
```

`bold-color = bright` makes bold text use the bright half of the palette, the way older terminals did. You can also set a fixed color.

### Transparency and blur

```ini
background-opacity = 0.9
background-blur = true
```

On macOS this needs a full restart. `background-blur = true` uses a blur radius of 20. A number gives you a specific radius. On macOS 26 there are two glass options:

```ini
background-blur = macos-glass-regular
```

Cells with an explicit background color stay opaque, so a Neovim theme that paints its own background will look solid. `background-opacity-cells = true` applies the opacity to those too.

Opacity turns off in native macOS fullscreen. The `toggle_background_opacity` action flips a transparent window to opaque and back, and it's in the command palette.

On Linux, blur works on KDE Plasma, where KWin's global blur setting applies and the intensity value is ignored. Other desktops aren't supported.

### Background image

Since 1.2:

```ini
background-image = /Users/flavio/Pictures/paper.jpg
background-image-opacity = 0.3
background-image-fit = cover
```

PNG and JPEG only. The image is per terminal, so with splits it repeats in each one. Fit options are `contain`, `cover`, `stretch`, and `none`, plus `background-image-position` and `background-image-repeat`.

### Themes and configs from other people

If the built-in themes aren't enough, or you want to see how other people set things up, there are a few places to look.

[ghostty.town](https://ghostty.town/) is a site where people share their whole Ghostty config, with a preview. You browse the popular ones, copy the lines you like, and submit yours if you want.

[awesome-ghostty](https://github.com/fearlessgeekmedia/awesome-ghostty) is a community list of themes, shaders, and tools. The original is no longer updated and points to a [maintained fork](https://github.com/wyattgill9/Awesome-Ghostty). The Themes section is where you find the ones that ship as separate files: [Catppuccin](https://github.com/catppuccin/ghostty) and [Rosé Pine](https://github.com/rose-pine/ghostty) in their official versions, Everforest, Gruvbox Material, Night Owl, Monokai, [the Kitty themes converted to Ghostty](https://github.com/hroi/ghostty-themes), and the base16 and base24 sets from tinted-terminal. Drop the file in `~/.config/ghostty/themes/` and use its name.

The same list has the tools I'd bother with. Converters if you're coming from another terminal: [ghostty-ghost](https://github.com/gambithunt/ghostty-ghost) moves settings over from Kitty and Alacritty, and [iterm-to-ghostty-colors](https://github.com/richscott/iterm-to-ghostty-colors) turns an iTerm2 color profile into Ghostty lines. Editor support: [ghostty-ls](https://github.com/MKindberg/ghostty-ls) is a language server for the config file, and [ghostty.nvim](https://github.com/isak102/ghostty.nvim) validates the config on save in Neovim. And an [unofficial web configurator](https://ghostty.zerebos.com/) if you'd rather click through the options than read `+show-config --default --docs`.

## The window

### Padding

```ini
window-padding-x = 10
window-padding-y = 8
```

Points, scaled for the display. Two values set different sides: `window-padding-x = 10,4` is 10 left and 4 right.

The window size is rarely an exact multiple of the cell size, so a few extra pixels end up at the right and bottom. `window-padding-balance = true` spreads them evenly on all sides.

The padding area is normally the background color. When a program like Neovim paints a different background, the padding shows a strip of your terminal color around it. `window-padding-color = extend` fixes that by extending the nearest cell's color into the padding.

### The macOS titlebar

`macos-titlebar-style` has four values:

```ini
macos-titlebar-style = tabs
```

`native` is the standard macOS titlebar. `transparent` (the default) lets your background color show through it. `tabs` merges the tab bar into the titlebar, which saves a row of vertical space. `hidden` removes the titlebar but keeps the rounded window frame.

With `hidden` you drag the window with Option+click on the edges, which is a macOS thing, not a Ghostty one.

`macos-window-buttons = hidden` removes the traffic lights. `macos-titlebar-proxy-icon = hidden` removes the little folder icon.

### Window decorations

```ini
window-decoration = none
```

This removes the titlebar and borders. On macOS it also disables tabs, because macOS tabs need a titlebar. On Linux, `auto`, `client`, and `server` pick how decorations are drawn. `auto` is right for almost everyone.

### Size and position

```ini
window-width = 120
window-height = 40
```

In cells, not pixels, and both must be set. This only affects new windows. `window-position-x` and `window-position-y` set the starting position in pixels on macOS, and those two options must also be set together.

`maximize = true` starts new windows maximized. `fullscreen = true` starts them fullscreen.

### Fullscreen on macOS

Cmd+Enter toggles fullscreen. By default that's native macOS fullscreen, with the animation and the separate space.

```ini
macos-non-native-fullscreen = true
```

This is instant and has no animation, but tabs don't work in it because the titlebar is gone. `visible-menu` keeps the menu bar. `padded-notch` avoids the notch on laptops.

### Working directory

New windows, tabs, and splits open in the directory of the terminal you were in. Since 1.3 each is a separate switch:

```ini
window-inherit-working-directory = false
tab-inherit-working-directory = true
split-inherit-working-directory = true
```

This needs shell integration, because that's how Ghostty learns where your shell is.

`working-directory` sets the folder for terminals that don't inherit. `home` and `inherit` are special values, or use a path.

### Save window state

On macOS, Ghostty can restore windows, tabs, and splits. With `window-save-state = default`, it saves them only when macOS forces termination or asks the app to restore state. Use `always` if you also want restoration after a normal quit, or `never` to disable it.

### Resize overlay

When you resize a window, a box shows the new size in columns and rows. `resize-overlay = never` hides it. `resize-overlay-position` moves it to a corner.

## Tabs

Cmd+T opens a tab. Cmd+W closes the current tab, split, or window, whichever is focused. Cmd+Shift+W closes the whole window. On Linux, Ctrl+Shift+T opens a tab, while Ctrl+Shift+W closes the entire tab and all its splits.

Move between tabs with Cmd+Shift+[ and Cmd+Shift+], or Ctrl+Tab and Ctrl+Shift+Tab. Cmd+1 through Cmd+8 jump to a tab by number, and Cmd+9 goes to the last one. On Linux the number shortcuts use Alt.

Double-click a tab title on macOS to rename it inline. Right-click a tab to give it a color. The command palette has "Change Tab Title" too.

```ini
window-new-tab-position = end
```

New tabs open after the current one by default. `end` puts them at the end of the list.

Closing a tab with a running process asks for confirmation. Sitting at a shell prompt doesn't, thanks to shell integration. `confirm-close-surface = always` asks every time, `false` never asks.

Undo works on tabs and splits. Cmd+Z brings back a tab you closed by accident, with its process still running, as long as you do it within the `undo-timeout`, which is 5 seconds by default. This is macOS only.

## Splits

Cmd+D splits the current terminal to the right. Cmd+Shift+D splits below. On Linux, Ctrl+Shift+O and Ctrl+Shift+E.

Move focus with Cmd+Option+Arrow in a direction, or Cmd+[ and Cmd+] to cycle in creation order. On Linux, Ctrl+Alt+Arrow.

Resize with Cmd+Ctrl+Arrow. Each press moves the divider 10 pixels. Cmd+Ctrl+= equalizes all splits.

Cmd+Shift+Enter zooms the focused split to fill the tab. Press again to unzoom. By default, moving to another split unzooms. This keeps the zoom and moves it to the new split instead:

```ini
split-preserve-zoom = navigation
```

Unfocused splits are dimmed to 70% so you can see where you are:

```ini
unfocused-split-opacity = 0.85
split-divider-color = #ff4b0a
```

Set the opacity to 1 to turn dimming off.

On macOS since 1.3 you can rearrange splits with the mouse. Drag a split into another position, into another tab, or out into a new window. The running process moves with it, and undo works here too.

`focus-follows-mouse = true` focuses a split when the pointer moves over it, without clicking.

## Scrollback

### Size

```ini
scrollback-limit = 100000000
```

Bytes, per terminal. The default is 10 MB. Memory is allocated lazily, so a big limit costs nothing until you fill it. There's no unlimited setting yet.

### Scroll with the keyboard

Cmd+Home and Cmd+End jump to the top and bottom. Cmd+Page Up and Cmd+Page Down move a page. On Linux, Shift+Home, Shift+End, Shift+Page Up, Shift+Page Down.

Cmd+Up and Cmd+Down jump between prompts. This is one of the best features once you're used to it. You ran a command with a lot of output, and you want to see the command before it. Cmd+Up takes you straight there. It needs shell integration. On Linux it's Ctrl+Shift+Page Up and Page Down.

Typing scrolls to the bottom. New output doesn't, so you can read old output while a build runs. `scroll-to-bottom = keystroke,output` changes that.

### Search

Cmd+F opens the search bar (Ctrl+Shift+F on Linux). Every match in the view is highlighted. Cmd+G and Cmd+Shift+G move to the next and previous match. Escape closes it. Cmd+E searches for the current selection.

Search is a 1.3 feature. Before that you had to pipe output through `grep` or dump the scrollback to a file.

```ini
search-background = #ffe082
search-selected-background = #f2a57e
```

These set the colors of candidate matches and the focused match.

### Scrollbars

Native scrollbars arrived in 1.3. They're overlaid on the content, so they don't steal a column. `scrollbar = system` follows your OS setting, which on macOS usually means visible only while scrolling. `never` hides them.

### Save the screen to a file

Cmd+Shift+J writes the visible screen to a temporary file and pastes its path into the terminal. Cmd+Ctrl+Shift+J copies the path instead. Cmd+Option+Shift+J opens the file in your editor.

The actions behind these are `write_screen_file`, `write_scrollback_file` for the entire history, and `write_selection_file`. Each takes `copy`, `paste`, or `open`:

```ini
keybind = cmd+shift+s=write_scrollback_file:open
```

Useful when a command produces more output than you want to read in the terminal, and you'd rather search it in your editor.

### Select a command's output

With shell integration, Cmd+triple-click on a command's output selects all of it. Handy for copying the whole result of a test run.

### Clear the screen

Cmd+K clears the screen and the scrollback. This is the terminal clearing itself, not the shell's `clear`, so it also works while a program is running.

## Keybindings

This is where Ghostty gets deep. Everything in the app can be bound to a key, and the binding system has features borrowed from tmux and Vim.

### The basic syntax

```ini
keybind = trigger=action
```

The trigger is modifiers and one key joined with `+`. The action is one of the actions from `ghostty +list-actions`, with an optional parameter after `:`.

```ini
keybind = cmd+shift+r=reload_config
keybind = ctrl+shift+enter=new_split:down
keybind = cmd+shift+z=toggle_split_zoom
```

Modifiers are `shift`, `ctrl`, `alt`, and `super`. Aliases work too: `cmd` and `command` for super, `opt` and `option` for alt, `control` for ctrl.

Duplicate triggers override the earlier one. `keybind = cmd+d=unbind` removes a binding, including a default one. `keybind = clear` wipes everything set so far, including the defaults, so put it first if you want to start from nothing.

### List the actions

```bash
ghostty +list-actions --docs
```

Ghostty 1.3.1 has 85 actions, each with a description. The ones you'll reach for: `new_tab`, `new_split`, `goto_split`, `resize_split`, `toggle_split_zoom`, `goto_tab`, `jump_to_prompt`, `scroll_page_up`, `copy_to_clipboard`, `paste_from_clipboard`, `increase_font_size`, `toggle_quick_terminal`, `toggle_command_palette`, `reload_config`, `open_config`.

### See the current bindings

```bash
ghostty +list-keybinds
```

Shows your bindings merged with the defaults. `--default` shows only the defaults, which is the fastest way to answer "what's the shortcut for X on this platform".

### Keys by character or by position

`a` matches whatever key produces the letter a in your keyboard layout. On an AZERTY keyboard that's the physical Q key. This is usually what you want.

`key_a` (or `KeyA`) matches the physical key where A sits on a US keyboard, regardless of layout. The names come from the W3C key code list: `bracket_right`, `digit_1`, `arrow_up`, `backquote`, `f5`, `page_down`.

Physical keys always win over character keys when both are bound.

This matters for international layouts. On an Italian keyboard the `+` key sits where a US keyboard has `]`, so `cmd+bracket_right` targets that physical key no matter what it prints.

### Send text or escape sequences

Three actions send bytes to the program instead of doing something in Ghostty:

```ini
keybind = cmd+backspace=text:\x15
keybind = alt+arrow_left=esc:b
keybind = cmd+arrow_up=csi:A
```

`text:` uses Zig string literal syntax, so `\x15` is Ctrl+U (delete to line start). `esc:b` sends Escape then `b`, which bash and zsh read as "word backwards". `csi:A` sends the cursor up sequence.

Ghostty's macOS defaults use this to make Cmd+Left and Cmd+Right go to line start and end, like a text field. Cmd+Backspace deletes to the start of the line.

### Key sequences (a leader key)

Separate triggers with `>` to require them in order:

```ini
keybind = ctrl+a>c=new_tab
keybind = ctrl+a>n=next_tab
keybind = ctrl+a>p=previous_tab
keybind = ctrl+a>-=new_split:down
keybind = ctrl+a>|=new_split:right
keybind = ctrl+a>z=toggle_split_zoom
keybind = ctrl+a>x=close_surface
keybind = ctrl+a>ctrl+a=text:\x01
```

Press Ctrl+A, then C, and you get a new tab. That's the tmux prefix model, and if you already know [tmux](https://flaviocopes.com/tmux/) your fingers will thank you.

The last line matters. Once `ctrl+a` is a prefix, pressing it alone does nothing. Ghostty waits forever for the next key. Binding `ctrl+a>ctrl+a` to `text:\x01` sends a real Ctrl+A to the shell when you press it twice, so you can still go to the start of the line.

An unbound key during a sequence sends everything through to the program as if no binding existed.

### Prefixes that change how a binding works

Put these before the trigger:

`global:` makes the binding work even when Ghostty isn't focused. This is how the quick terminal is meant to be opened. On macOS it needs Accessibility permission, which Ghostty asks for. On Linux it needs a desktop that implements the Global Shortcuts portal (KDE Plasma 5.27 and newer, GNOME 48 and newer).

```ini
keybind = global:cmd+backquote=toggle_quick_terminal
```

`all:` sends the action to every terminal, not only the focused one. For example, `keybind = all:cmd+shift+w=close_window` closes every window.

`unconsumed:` runs the action but also passes the key to the program. `unconsumed:cmd+s=reload_config` reloads the config and still sends Cmd+S to whatever is running.

`performable:` only takes the key if the action can do something. With `performable:cmd+c=copy_to_clipboard`, Cmd+C copies when there's a selection and otherwise goes through to the program. The macOS defaults use this for copy and paste. The Linux defaults also use it for split navigation when there's only one split.

Prefixes can stack, but `global:` and `all:` bindings always consume the key. Adding `unconsumed:` to either one does not pass the key through.

### Chain actions

Since 1.3, one key can run several actions in order:

```ini
keybind = cmd+shift+n=new_window
keybind = chain=toggle_fullscreen
```

Each `chain` appends to the most recent binding. You can chain as many as you like, and chains work with sequences too.

### Key tables (modal bindings)

Also since 1.3. A key table is a named set of bindings you activate on demand. Inside a table, keys mean something else until you leave.

Here is a resize mode:

```ini
keybind = resize/arrow_up=resize_split:up,20
keybind = resize/arrow_down=resize_split:down,20
keybind = resize/arrow_left=resize_split:left,20
keybind = resize/arrow_right=resize_split:right,20
keybind = resize/equal=equalize_splits
keybind = resize/escape=deactivate_key_table
keybind = resize/catch_all=ignore

keybind = cmd+shift+r=activate_key_table:resize
```

Cmd+Shift+R enters resize mode. Arrows resize in bigger steps than the defaults, `=` equalizes, Escape leaves. `catch_all=ignore` swallows every other key so nothing leaks into the shell while you're in the mode. Ghostty shows an indicator while a table is active, so you know which mode you're in.

`activate_key_table_once:resize` is the one-shot version. It leaves the table after the first binding fires.

Table names go before a `/`. Bindings in the default table stay available inside a custom table unless you shadow them.

### catch_all

`catch_all` matches any key that isn't otherwise bound. Combined with modifiers, `ctrl+catch_all=ignore` eats every unbound Ctrl combination. Inside a sequence, `ctrl+a>catch_all=end_key_sequence` ends the sequence and sends the Ctrl+A through, without the extra key.

### Remap modifiers

```ini
key-remap = ctrl=super
```

This makes the physical Ctrl key act as Cmd inside Ghostty only. It's one way, so Cmd stays Cmd. Sided names like `left_ctrl` and `right_alt` work too. It doesn't change what characters keys produce, only how modifiers are read.

One catch on macOS: menu shortcuts fire before the remap, because macOS handles the menu itself. If a remapped combination collides with a menu item, unbind that item and rebind it with the modifier you want.

### Debug a binding with the inspector

Cmd+Option+I (Ctrl+Shift+I on Linux) opens the terminal inspector. Its Keyboard tab shows every key event as Ghostty sees it, with the modifiers and what was sent to the program. When a binding doesn't fire, this tells you what Ghostty received.

The inspector also shows the raw VT stream and, since 1.3, an overlay of the shell integration prompt regions.

## Shell integration

Shell integration is a small script Ghostty loads into your shell. It uses escape sequences to tell Ghostty where the prompt starts, when a command begins and ends, and what the current directory is.

You get:

- New tabs and splits in the current directory
- Cmd+Up and Cmd+Down between prompts
- No close confirmation at an idle prompt
- A bar cursor at the prompt
- Prompts that redraw cleanly on resize
- Cmd+triple-click on macOS, or Ctrl+triple-click on Linux, to select command output
- Click in the prompt to move the cursor
- Command finished notifications, if you enable them

### How it's injected

Ghostty looks at the name of the command it's about to run. If it's `bash`, `zsh`, `fish`, or `nu`, it injects the matching script through environment variables. Nothing is written to your dotfiles.

For Elvish, Ghostty adds its module directory to `XDG_DATA_DIRS`, but it does not load the module. Add this to `~/.config/elvish/rc.elv`:

```elvish
use ghostty-integration
```

```ini
shell-integration = detect
```

`detect` is the default. Set a shell name to force it when your shell has an unusual binary name. `none` disables it.

The Bash that ships with macOS at `/bin/bash` can't use automatic injection because of an Apple-specific change to how it handles `ENV` for interactive shells. Install Bash from Homebrew or source the script manually.

### Check that it's working

Inside Ghostty:

```bash
echo $TERM_PROGRAM $TERM $GHOSTTY_RESOURCES_DIR
```

```text
ghostty xterm-ghostty /Applications/Ghostty.app/Contents/Resources/ghostty
```

Then open a new tab from a subfolder. If it opens in that folder, integration works.

### Source it manually

Automatic injection only covers the first shell. If you start another shell from it, or switch shells with a tool like `nix-shell`, the new shell has no integration. Sourcing the script in your shell config fixes that. Ghostty sets `GHOSTTY_RESOURCES_DIR`, so you can guard on it.

Fish, in `~/.config/fish/config.fish`:

```fish
if set -q GHOSTTY_RESOURCES_DIR
    source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish"
end
```

Zsh, in `~/.zshrc`:

```zsh
if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
  source "$GHOSTTY_RESOURCES_DIR/shell-integration/zsh/ghostty-integration"
fi
```

Bash, at the top of `~/.bashrc`:

```bash
if [ -n "${GHOSTTY_RESOURCES_DIR}" ]; then
    builtin source "${GHOSTTY_RESOURCES_DIR}/shell-integration/bash/ghostty.bash"
fi
```

If you want to know more about fish, I wrote a [complete guide to the Fish shell](https://flaviocopes.com/fish-shell/).

### Features

```ini
shell-integration-features = cursor,no-sudo,title,no-ssh-env,no-ssh-terminfo,path
```

That's the default. Each one can be turned on or off with the `no-` prefix.

`cursor` sets the bar cursor at the prompt. `title` sets the window title from the shell. `path` adds Ghostty's binary folder to `PATH`, which is why `ghostty +version` works on macOS. `sudo` wraps sudo so it keeps the `TERMINFO` variable, fixing the "unknown terminal type" error under sudo. `ssh-env` and `ssh-terminfo` are covered in the SSH section.

### Command finished notifications

New in 1.3, and disabled by default. Enable it, start a long build, switch to your browser, and Ghostty tells you when it's done:

```ini
notify-on-command-finish = unfocused
notify-on-command-finish-action = no-bell,notify
notify-on-command-finish-after = 30s
```

`unfocused` notifies only when the terminal isn't focused, which is the useful setting. `always` and `never` are the alternatives. The action can be a bell, a desktop notification, or both. The duration filters out short commands. Nobody needs a notification for `ls`.

### Click to move the cursor

Also 1.3. Click anywhere in the current prompt line and the cursor moves there, like a text field. Fish 4.1 and Nushell 0.111 support this natively. Bash and zsh get it through the Ghostty scripts. `cursor-click-to-move = false` turns it off.

## The quick terminal

A drop-down terminal that slides in from the edge of the screen with a global shortcut, and hides again when you press it a second time. Same idea as Quake's console, or iTerm2's hotkey window.

There's no default binding. Add one:

```ini
keybind = global:cmd+backquote=toggle_quick_terminal
```

`global:` means it works from any app. On macOS the first time you'll be asked for Accessibility permission.

```ini
quick-terminal-position = bottom
quick-terminal-size = 40%
quick-terminal-animation-duration = 0
quick-terminal-autohide = true
```

Position can be `top`, `bottom`, `left`, `right`, or `center`. Size is a percentage or pixels, and you can give two values like `50%,900px`. On macOS, set the animation duration to 0 if the slide bothers you. That option has no effect on Linux, where slide animation only works through KDE's Sliding Popups effect. `autohide` hides the quick terminal when it loses focus. On macOS, `quick-terminal-screen = mouse` opens it on the screen where the pointer is, useful with several monitors.

Changing the position needs a restart on macOS.

Some limits. Only one quick terminal exists. It doesn't restore after a restart. On macOS it has no tabs, because tabs need a titlebar, but splits work. On Linux it only works on Wayland compositors that support `wlr-layer-shell`, which rules out GNOME.

Inside the quick terminal the `GHOSTTY_QUICK_TERMINAL` variable is set, so your prompt can look different there.

If you want Ghostty to be only a quick terminal, `macos-hidden = always` removes it from the Dock and app switcher.

## The command palette

Cmd+Shift+P (Ctrl+Shift+P on Linux) opens a searchable list of most actions useful in an interactive session, with each shortcut if it has one. It is not the complete action list. Type a few letters, press Enter. It's the fastest way to find an action you use rarely, like copying the screen as HTML or toggling read-only mode.

On macOS the palette also lists your open terminals. Type part of a title or a working directory and jump to it.

Add your own entries:

```ini
command-palette-entry = title:"Reset styles",description:"Clear stuck colors and attributes",action:"csi:0m"
command-palette-entry = title:"Split for logs",description:"Open a split below",action:"new_split:down"
```

Fields are `title`, `description`, and `action`. Quote the values, as the built-in entries do, so commas and colons inside them don't confuse the parser. Setting `command-palette-entry =` with no value removes the defaults.

## Clipboard and mouse

### Copy and paste

Cmd+C and Cmd+V, or Ctrl+Shift+C and Ctrl+Shift+V on Linux. Ctrl+C on Linux still kills the process, which is why the copy shortcut has Shift.

Since 1.3 a copy puts both plain text and HTML on the clipboard, so pasting into a rich text editor keeps colors. If a program you paste into misbehaves, bind copy to plain text only:

```ini
keybind = cmd+c=copy_to_clipboard:plain
```

Formats are `mixed`, `plain`, `html`, and `vt`. `vt` includes the escape sequences, for pasting styled text into another terminal.

```ini
copy-on-select = clipboard
```

The default is `true`. It copies to the selection clipboard on Linux, which is the one middle-click normally pastes from, and to the system clipboard on macOS. `clipboard` copies to both. `false` turns it off. On GTK, the desktop setting `gtk-enable-primary-paste` can disable middle-click paste.

`clipboard-trim-trailing-spaces` is on by default and strips trailing whitespace from copied lines. `selection-clear-on-copy = true` deselects after copying.

### Paste protection

Pasting text with newlines into a shell runs every line. Ghostty asks first when the paste looks unsafe. `clipboard-paste-protection = false` turns that off. Programs with bracketed paste mode on, like most shells and editors, are considered safe already.

### Programs writing to your clipboard

Tools like Neovim and tmux use OSC 52 to write to the system clipboard, which is how clipboard sharing works over SSH.

```ini
clipboard-write = allow
clipboard-read = ask
```

Writing is allowed by default. Reading asks you, because a program that can read your clipboard can read a password you just copied.

### Right click

```ini
right-click-action = copy-or-paste
```

The default shows a context menu. `copy-or-paste` copies when there's a selection and pastes otherwise, which matches Windows Terminal. `paste`, `copy`, and `ignore` are the other values.

### Word selection

Double-click selects a word. `selection-word-chars` (1.3) lists the characters that end a word. The default stops at spaces, quotes, brackets, `|`, `$`, and punctuation like `:;,`. To select `user:password` as one word, set the whole list again without the colon:

```ini
selection-word-chars = " \t'\"│`|;,()[]{}<>$"
```

### Links

Hold Cmd (Ctrl on Linux) and hover a URL to underline it, then click to open it in your browser. A preview of the destination appears at the bottom. Cmd+click also opens file paths, including ones with `~`.

`link-previews = osc8` limits the preview to hyperlinks made with OSC 8, where the visible text differs from the destination. `false` hides previews.

### Mouse in TUI programs

Programs like Vim, htop, and lazygit can capture the mouse. Then a drag selects inside the program instead of in Ghostty. With the default `mouse-shift-capture = false`, hold Shift to force a Ghostty selection. A TUI can request Shift for itself through `XTSHIFTESCAPE`; use `mouse-shift-capture = never` if you always want Shift to select in Ghostty.

```ini
mouse-reporting = false
```

This stops programs from getting mouse events at all. The `toggle_mouse_reporting` action flips it at runtime, and there's a palette entry for it.

`mouse-hide-while-typing = true` hides the pointer as soon as you type. `mouse-scroll-multiplier = 2` scrolls faster with a wheel. You can set it separately for precision devices like a trackpad: `precision:1,discrete:5`.

## SSH and terminfo

Ghostty sets `TERM=xterm-ghostty`. That name tells programs which capabilities the terminal has, by looking up an entry in the terminfo database.

The problem is that most servers don't have that entry yet. It landed in ncurses at the end of 2024, so it will spread with distro updates, but right now when you SSH into a server you often see:

```text
missing or unsuitable terminal: xterm-ghostty
```

Programs like `less`, `vim`, and `htop` fail or complain. You have three fixes.

### Fix 1: copy the terminfo to the server

```bash
infocmp -x xterm-ghostty | ssh myserver -- tic -x -
```

`infocmp` dumps the local entry, `tic` compiles it on the server. Run it once per server. You may see a warning about "older tic versions" from `tic`, which is safe to ignore.

On macOS before Sonoma, the built-in `infocmp` is too old for this entry. Install `ncurses` with Homebrew and use its `infocmp` instead.

This is the best option because the server gets the full Ghostty capabilities, including styled underlines.

### Fix 2: send a different TERM to that host

In `~/.ssh/config`:

```text
Host myserver
  SetEnv TERM=xterm-256color
```

Needs OpenSSH 8.7 or newer. The server sees a terminal type it knows. You lose the features xterm doesn't advertise, but everything works.

### Fix 3: let Ghostty do it

```ini
shell-integration-features = ssh-env,ssh-terminfo
```

With these on, Ghostty's shell integration wraps `ssh`. `ssh-terminfo` tries to install the terminfo on the remote host the first time you connect, using `infocmp` locally and `tic` remotely, and remembers the hosts where it succeeded. Until that host is in the cache, the wrapper uses `TERM=xterm-256color`. With `ssh-env` enabled, it also sends `COLORTERM`, `TERM_PROGRAM`, and `TERM_PROGRAM_VERSION`.

The cache of hosts is managed with an action:

```bash
ghostty +ssh-cache
ghostty +ssh-cache --remove='flavio@203.0.113.10'
ghostty +ssh-cache --clear
```

List the cache first and copy the exact `user@hostname` key it shows. The wrapper resolves SSH aliases before it creates that key, so removing the alias itself may do nothing.

Whether the remote accepts the environment variables depends on the server's `sshd_config` (`AcceptEnv`). If you're setting up servers regularly, my [SSH for developers](https://flaviocopes.com/ssh-for-developers/) guide covers that side.

### sudo

Same error, different cause. `sudo` resets the environment and drops `TERMINFO`. Either add `Defaults env_keep += "TERMINFO"` to your sudoers, or turn on the `sudo` shell integration feature and Ghostty wraps sudo for you.

## What programs can do inside Ghostty

This section is for the tools running inside the terminal rather than the terminal itself. Ghostty implements the modern protocols, so the tools that use them work at their best.

Images. The Kitty graphics protocol lets programs draw real images in the terminal. Yazi shows image previews, timg displays pictures, and some Neovim plugins render diagrams. `image-storage-limit` caps the memory per screen (320 MB by default). Set it to 0 to disable images.

Keyboard. The Kitty keyboard protocol gives programs unambiguous key events, so Neovim can tell Ctrl+I from Tab and see Shift+Enter. Helix, Neovim, and Zellij use it.

Hyperlinks. OSC 8 lets a program print clickable text with a hidden URL. GNU `ls --hyperlink` on Linux and `rg --hyperlink-format=default` do this. macOS ships BSD `ls`, which has no `--hyperlink` option. You Cmd+click the file name in Ghostty.

Synchronized output. Mode 2026 lets a program tell the terminal "don't paint until I'm done", which removes tearing in editors and TUI apps.

Notifications. OSC 9 and OSC 777 let a program send a desktop notification. `desktop-notifications = false` blocks it.

Progress bars. The ConEmu OSC 9;4 sequence lets a program show a progress indicator for a long task. `progress-style = false` ignores it.

Light and dark. Programs can ask the terminal which color scheme is in use and get notified when it changes. This is how Neovim can switch its own theme when you switch macOS to dark mode.

Underlines. Curly, dotted, and dashed underlines in color, used by editors for diagnostics.

Prompt marking. OSC 133 is how the shell tells Ghostty about prompts and commands, and it's the basis for prompt jumping, output selection, and finish notifications.

Clipboard. OSC 52, covered above.

Title. Programs can set the window title. `title = " "` (a space) forces a blank title and ignores them. `title-report` lets programs read the title back, and it's off by default because it's a known attack vector.

Ghostty documents its implemented sequences in the [VT reference](https://ghostty.org/docs/vt/reference). The project labels that reference as incomplete, so treat it as a useful starting point rather than an exhaustive list.

## Run a command instead of a shell

```ini
command = /opt/homebrew/bin/fish
```

`command` is what every new terminal runs. If you leave it unset, Ghostty uses your `SHELL` variable or the login shell from the system. Arguments are fine, and the command runs through `/bin/sh -c` when it has them. `direct:nvim notes.md` skips the shell. `shell:` forces one.

`initial-command` is the same but only for the first terminal after launch.

From the command line, `-e` runs a command in a new instance:

```bash
ghostty -e top
```

On macOS, from another terminal:

```bash
open -na Ghostty.app --args -e top
```

When the command exits the window closes. `wait-after-command = true` keeps it open until you press a key, which is useful for a script you launch from a Finder icon and want to read the output of.

```ini
env = EDITOR=nvim
env = PAGER=less
input = raw:echo hello\n
working-directory = ~/dev
```

`env` adds environment variables to every terminal. `input` (1.2 and later) types text into the terminal at startup. `path:` reads it from a file instead of `raw:`.

## Notifications and the bell

A program rings the bell by printing `\a`. Ghostty can respond in several ways:

```ini
bell-features = attention,title,border,audio
bell-audio-path = ~/sounds/ping.wav
bell-audio-volume = 0.4
```

`attention` bounces the Dock icon once when Ghostty isn't focused (on Linux it depends on the desktop). `title` puts a 🔔 in the tab title until you interact. `border` draws a border around the terminal that rang. `audio` plays your file, on macOS since 1.3. `system` uses the system alert sound.

The defaults are `attention` and `title`, no border and no sound. A relative audio path is resolved from the config file's folder, and `~/` works.

## Custom shaders

Ghostty can run GLSL fragment shaders over the rendered terminal, in the Shadertoy format. This is how people get CRT curvature, scanlines, glowing cursors, and cursor trails.

Write a `mainImage` function. `iChannel0` is the rendered terminal, `iResolution` its size, `iTime` the seconds since start. A vignette:

```glsl
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec4 color = texture(iChannel0, uv);
  float d = distance(uv, vec2(0.5));
  fragColor = color * (1.0 - d * 0.4);
}
```

Save it and point the config at it:

```ini
custom-shader = /Users/flavio/.config/ghostty/shaders/vignette.glsl
custom-shader-animation = false
```

Several `custom-shader` lines run in order, each receiving the previous output. `custom-shader-animation = true` keeps redrawing so time-based shaders animate, at some CPU cost. `false` redraws only on terminal updates. `always` animates even unfocused terminals.

Ghostty adds uniforms that Shadertoy doesn't have: `iCurrentCursor` and `iPreviousCursor` with position and size, `iTimeCursorChange`, `iCurrentCursorColor`, `iFocus`, the whole `iPalette`, and the background and foreground colors. These are what make cursor trail shaders possible.

A broken shader is ignored, and the error only shows in the logs. If the window goes black, remove the `custom-shader` line and reload.

You don't have to write them yourself. The Shaders section of [awesome-ghostty](https://github.com/fearlessgeekmedia/awesome-ghostty) collects ready-made ones: [hackr-sh/ghostty-shaders](https://github.com/hackr-sh/ghostty-shaders) has the cursor effects most people are after (`cursor_blaze.glsl`, `cursor_lightning.glsl`, a smear cursor) plus bloom, CRT, matrix, and starfield variants, and there's a separate [CRT shader](https://github.com/luiscarlospando/crt-shader-with-chromatic-aberration-glow-scanlines-dot-matrix) with scanlines and chromatic aberration if you want the old monitor look. Download the `.glsl` file, point `custom-shader` at it, reload.

## macOS specifics

### The Option key

On a US layout, Option+B types `∫`. Terminal programs want Option to work as Alt, so Alt+B means "back a word" in your shell.

```ini
macos-option-as-alt = true
```

`left` and `right` let you keep one Option key for special characters and use the other as Alt. When unset, Ghostty picks `true` for the US layouts and `false` for everything else, because on most European layouts you need Option to type characters like `@` and `[`.

### Secure input

When Ghostty detects a password prompt, it enables macOS Secure Input, which stops other apps from reading your keystrokes. `macos-auto-secure-input = false` turns off the detection, and Ghostty > Secure Keyboard Entry in the menu toggles it by hand. The detection is a heuristic and it doesn't work over SSH, so for a password on a server turn it on from the menu.

### The app icon

```ini
macos-icon = retro
```

Variants are `blueprint`, `chalkboard`, `microchip`, `glass`, `holographic`, `paper`, `retro`, and `xray`. `custom` uses your own `.icns` or PNG from `macos-custom-icon`. `custom-style` recolors the official icon with `macos-icon-ghost-color` and `macos-icon-screen-color`. `macos-icon-frame` chooses the frame material.

### Default terminal

The Ghostty menu has "Set Ghostty as Default Terminal App" since 1.3. After that, opening a `.command` file or a folder with "Open in Terminal" services uses Ghostty.

### Shortcuts and AppleScript

Ghostty exposes actions to the Shortcuts app: create terminals, send text, run commands, and invoke keybind actions. The available actions depend on your macOS version. Command-palette actions need macOS 14, while New Terminal and its command parameter need macOS 15. `macos-shortcuts = ask` prompts the first time, then remembers.

AppleScript support arrived in 1.3 as a preview. You can address windows, tabs, and terminals, and send them text:

```applescript
tell application "Ghostty"
    set term to focused terminal of selected tab of front window
    input text "git status\n" to term
end tell
```

`macos-applescript = false` disables it. macOS asks for Automation permission before another app can control Ghostty.

### Updates

Ghostty updates itself on macOS through Sparkle. `auto-update = check` notifies you, `download` also fetches the update, `off` disables it. Ghostty doesn't add any tracking to this: it downloads the version info and compares it locally. "Check for Updates" is in the command palette.

```ini
auto-update-channel = tip
```

`tip` gets a build from every commit to the main branch. It's what the private beta ran on, and it's how you get features before the next stable release. Needs a restart to take effect.

### Small things

`macos-window-shadow = false` removes the shadow, which looks better with transparency. `macos-dock-drop-behavior = new-window` opens a new window when you drop a folder on the Dock icon, instead of a tab. `window-step-resize = true` resizes in whole cells. `toggle_window_float_on_top` keeps a window above everything else. `toggle_visibility` hides and shows all windows.

## Linux specifics

Ghostty on Linux needs GTK 4.14 or newer and libadwaita 1.5 or newer. It follows the system light or dark appearance and works on both Wayland and X11. Arbitrary third-party GTK themes aren't guaranteed to carry through libadwaita.

### One process or many

```ini
gtk-single-instance = detect
```

With single instance, running `ghostty` again opens a new window in the existing process instead of a second app. `detect` chooses a separate process when you pass CLI flags or `TERM_PROGRAM` is already set. That environment check is a heuristic for launching Ghostty from another terminal. `ghostty +new-window` opens a window in the running instance, which is handy from a script or a launcher.

### Titlebar and tabs

```ini
gtk-titlebar-style = tabs
gtk-tabs-location = bottom
gtk-wide-tabs = false
gtk-titlebar-hide-when-maximized = true
```

`tabs` merges tabs into the titlebar like the macOS option. `gtk-tabs-location = hidden` replaces the bar with a tab counter button that opens the tab overview. `window-show-tab-bar = always` shows the bar even with one tab. `gtk-toolbar-style` picks `flat`, `raised`, or `raised-border`.

The tab overview (`toggle_tab_overview`) is a grid of all your tabs, GNOME style.

### Decorations

`window-decoration = server` asks the compositor to draw the titlebar, which KDE and most non-GNOME desktops support. `client` draws it with GTK. `none` removes it, which tiling window manager users usually want.

### Cgroups

```ini
linux-cgroup = always
linux-cgroup-memory-limit = 4294967296
```

Each terminal can run in its own systemd scope. The default is `single-instance`, so Ghostty attempts this when it runs as a single instance. The memory limit is a soft `MemoryHigh` limit in bytes. It does not kill the tab by itself. Configure an OOM monitor such as `systemd-oomd` if you want pressured scopes terminated, and remember that Ghostty continues if cgroup creation fails.

### Custom CSS

```ini
gtk-custom-css = /home/flavio/.config/ghostty/ghostty.css
```

GTK widgets are styled with CSS, so you can restyle the tab bar or titlebar. Run `env GTK_DEBUG=interactive ghostty` to get the GTK inspector and see what to target. This syntax works in fish too.

### Quick terminal and global shortcuts

The quick terminal needs Wayland and a compositor with `wlr-layer-shell`. KDE, Hyprland, and Sway work. GNOME doesn't. `gtk-quick-terminal-layer` picks where it stacks. Slide animations only happen on KDE with the Sliding Popups effect.

Global keybinds need the XDG Global Shortcuts portal. KDE Plasma 5.27 and GNOME 48 have it. Other desktops depend on their portal implementation, so check yours before spending time on a `global:` binding that will never fire.

`quick-terminal-autohide` defaults to false on Linux, because global shortcuts are harder to set up there and you may want the terminal to stay while you go set it up.

### GTK keyboard defaults

Since Ctrl+C has to reach the shell, most shortcuts use Ctrl+Shift. The full table is in the Default shortcuts section near the start of the article. `ghostty +list-keybinds --default` prints them for your version.

## Ghostty and coding agents

I run coding agents in the terminal most of the day, so this matters to me.

Agents like Claude Code, Codex, and cursor-agent produce a lot of output with hyperlinks, emoji, and colored regions. Ghostty 1.3 fixed a memory leak that showed up with Claude Code running for hours. If you noticed Ghostty's memory growing with an agent open, update.

An agent can also help you configure Ghostty. Ghostty ships no official MCP server or agent plugin, although community projects exist. The CLI already gives an agent everything it needs. If you keep your dotfiles in a repo, this is what I'd put in its `AGENTS.md`:

```markdown
## Ghostty

- The preferred config file is `~/.config/ghostty/config.ghostty`.
- Run `ghostty +show-config --default --docs` for the full option reference of the installed version. Do not rely on memory for option names.
- Validate changes with `ghostty +validate-config` before finishing.
- List actions with `ghostty +list-actions --docs` and current bindings with `ghostty +list-keybinds`.
- There is no `+reload-config` CLI action. On macOS, reload with Cmd+Shift+, or use AppleScript to perform the `reload_config` action on the focused terminal.
```

The `+edit-config` action opens the file but does not reload it. An agent on macOS can trigger the reload itself:

```bash
osascript -e 'tell application "Ghostty" to perform action "reload_config" on focused terminal of selected tab of front window'
```

On Linux and FreeBSD, the GTK app reloads its configuration when it receives `SIGUSR2`.

The `TERM_PROGRAM=ghostty` variable lets a script or an agent detect it's running inside Ghostty and use features like OSC 8 links or images.

On macOS, AppleScript and Shortcuts make Ghostty scriptable from outside. An agent can type into a specific terminal without a separate Python API. iTerm2 has long supported targeted input through AppleScript too.

If you run several agents at once and want notification rings, workspaces, and a browser next to the terminals, that's what [cmux](https://flaviocopes.com/cmux/) does, on top of libghostty. Ghostty itself stays a terminal.

## Ghostty with tmux

Ghostty has tabs and splits, so do you still need [tmux](https://flaviocopes.com/tmux/)?

For local work, Ghostty's splits are nicer: native, mouse-friendly, and they don't interfere with scrollback or copy and paste the way tmux panes do. The key sequences let you drive them with a prefix key if that's what your fingers know.

The main reason I still use tmux is session persistence. Close Ghostty, or lose an SSH connection, and a tmux session keeps running on the server. Ghostty can't do that. Tmux also provides sharing, scripting, remote control, and reusable layouts.

Ghostty has no finished tmux control-mode integration yet. Control mode is a tmux protocol that iTerm2 uses to show tmux windows as native tabs. Ghostty 1.3.1 contains parser groundwork, but it isn't connected to the desktop interface.

The way I'd split it: Ghostty tabs and splits locally, tmux on servers.

## Common problems

**Option+key types a strange character instead of working as Alt.** Set `macos-option-as-alt = true`, or `left` to keep the right Option for special characters.

`'xterm-ghostty': unknown terminal type` **on a server.** See the SSH section. The one-liner with `infocmp` and `tic` is the quickest fix.

**Same error with sudo.** Turn on the `sudo` shell integration feature or preserve `TERMINFO` in sudoers.

**New tabs don't open in the current folder.** Shell integration isn't running. Check `echo $GHOSTTY_RESOURCES_DIR`. Nested shells normally inherit this variable but lose automatic integration, so source the script manually when the variable exists. If it's empty, Ghostty's resource environment is missing or has been cleared.

**A config change did nothing after reload.** Some options apply only to new terminals (padding, `font-codepoint-map`, `scrollback-limit`), and `background-opacity` and `quick-terminal-position` need a restart on macOS. Check with `ghostty +show-config` that the value actually loaded, and with `+validate-config` that the file parses.

**The** `ghostty` **command isn't found.** On macOS it's only on `PATH` inside Ghostty, through the `path` shell integration feature. Elsewhere use `/Applications/Ghostty.app/Contents/MacOS/ghostty`, or symlink it.

**Tabs disappeared after going fullscreen.** You're using non-native fullscreen or `window-decoration = none`. Both remove the titlebar, and macOS tabs live in the titlebar.

**Text looks thin on macOS.** `font-thicken = true`.

**A** `global:` **keybind does nothing on macOS.** Grant Accessibility permission in System Settings > Privacy & Security > Accessibility. Ghostty asks once, and if you dismissed it you have to add it by hand.

**Ctrl+Shift shortcuts on Linux conflict with an app.** Rebind. `keybind = ctrl+shift+e=unbind` frees a key for the program.

**Vim can't tell some keys apart.** Ghostty supports the Kitty keyboard protocol. In Neovim it's on automatically. In Vim, check `:help keyprotocol`.

**The terminal is stuck in a weird state after a crashed program.** The `reset` action fixes it, same as the `reset` command. It's in the command palette.

**I want the old plain text copy.** `keybind = cmd+c=copy_to_clipboard:plain`. The 1.3 default copies both plain text and HTML.

## A complete starter config

Here is a config that pulls together the pieces from this guide. Copy what you want, leave the rest.

```ini
# ~/.config/ghostty/config.ghostty

# Font
font-family = JetBrains Mono
font-size = 14
font-feature = -calt
adjust-cell-height = 5%

# Theme
theme = light:Catppuccin Latte,dark:Catppuccin Mocha
minimum-contrast = 1.1
cursor-style = bar
cursor-style-blink = false

# Window
window-padding-x = 8
window-padding-y = 6
window-padding-balance = true
window-padding-color = extend

# Scrollback
scrollback-limit = 50000000

# Clipboard
copy-on-select = clipboard
right-click-action = copy-or-paste

# Shell integration
shell-integration-features = cursor,sudo,title,ssh-env,ssh-terminfo,path
notify-on-command-finish = unfocused
notify-on-command-finish-action = no-bell,notify
notify-on-command-finish-after = 20s

# Quick terminal
keybind = global:cmd+backquote=toggle_quick_terminal
quick-terminal-position = bottom
quick-terminal-size = 40%
quick-terminal-animation-duration = 0

# Splits
unfocused-split-opacity = 0.85
split-preserve-zoom = navigation

# Resize mode
keybind = resize/arrow_up=resize_split:up,20
keybind = resize/arrow_down=resize_split:down,20
keybind = resize/arrow_left=resize_split:left,20
keybind = resize/arrow_right=resize_split:right,20
keybind = resize/equal=equalize_splits
keybind = resize/escape=deactivate_key_table
keybind = resize/catch_all=ignore
keybind = cmd+shift+r=activate_key_table:resize

# Machine-specific overrides, not in git
config-file = ?local
```

Run `ghostty +validate-config` after saving, then Cmd+Shift+, to reload.

That's the whole tour. Ghostty gives you a fast window with good defaults and a config file that does what you tell it. What you do inside that window is the part that matters, and that's what the guides at the start of this article are for.
