A deep dive into Ghostty
By Flavio Copes
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.
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), 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, for example, uses libghostty for its terminal panes. And it’s not alone: 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 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, which has the download for macOS and the install instructions for Linux.

macOS
Download the .dmg from ghostty.org/download and drag the app into Applications. Or use Homebrew:
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:
# 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.
Check the version
Open Ghostty and run:
ghostty +version
You get the version plus some build details:
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

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:
+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

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:
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 is the overview. If you’ve never opened a terminal, start with The Command Line for Complete Beginners instead. The free Shell Commands course goes through the commands one lesson at a time, and the CLI 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, cd, grep, find, tail, chmod, ps, kill, and tar. The full list is under the CLI tag.
Then the shell. The Unix Shells Tutorial explains what a shell is and how bash, zsh, and fish differ. I use fish, and The Fish Shell: A Complete Guide is how I’d set it up today. If you’d rather stay with bash, read The Bash shell and Introduction to Bash Shell Scripting, then the Shell Scripting and Automation course. Two small ones you’ll need on day one: how to set an alias and how to set environment variables.
Tools that live in the terminal. tmux for sessions that survive, and I compare it with Ghostty’s own splits later in this guide. cmux if you run several coding agents at once. zoxide to stop typing long cd paths. jq 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 covers keys, config, and tunnels, and the SSH section of this guide covers the Ghostty-specific part. To go further, the free Linux Basics, VPS, Linux Server Troubleshooting, and Build a 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 covers the setup around the terminal: Homebrew, dotfiles, the tools worth installing. A practical guide to 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 and the Git Cheat Sheet cover it.
The books and courses are all on the free library page. 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:
~/.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:
~/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.
# 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:
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:
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:
ghostty +validate-config
No output and exit code 0 means the file is fine. With a mistake, you get the file, line, and problem:
/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
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:
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:
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:
ghostty --font-size=20 --theme="Rose Pine"
On macOS you can’t launch the app that way from a shell. Use open:
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:
ghostty +list-fonts
You get every family with its styles, one block per family:

Then set it:
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:
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:
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:
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:
ghostty +show-face --string="→ λ ⚡"
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
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:
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:
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:
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:
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:
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:
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:
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:
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:

Set a theme
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:
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:
# ~/.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:
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:
theme = Nord
background = #1a1d24
cursor-color = #ff4b0a
palette sets one of the 256 colors. The first 16 are the ANSI colors programs use most:
palette = 1=#e92820
palette = 9=#ff4b0a
Colors accept #RRGGBB, RRGGBB, or X11 names like orange. ghostty +list-colors prints the names.
Cursor
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
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:
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
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
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:
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:
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 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 is a community list of themes, shaders, and tools. The original is no longer updated and points to a maintained fork. The Themes section is where you find the ones that ship as separate files: Catppuccin and Rosé Pine in their official versions, Everforest, Gruvbox Material, Night Owl, Monokai, the Kitty themes converted to Ghostty, 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 moves settings over from Kitty and Alacritty, and iterm-to-ghostty-colors turns an iTerm2 color profile into Ghostty lines. Editor support: ghostty-ls is a language server for the config file, and ghostty.nvim validates the config on save in Neovim. And an unofficial web configurator if you’d rather click through the options than read +show-config --default --docs.
The window
Padding
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:
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
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
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.
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:
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.
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:
split-preserve-zoom = navigation
Unfocused splits are dimmed to 70% so you can see where you are:
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
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.
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:
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
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 :.
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
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
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:
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:
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 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).
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:
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:
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
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:
use ghostty-integration
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:
echo $TERM_PROGRAM $TERM $GHOSTTY_RESOURCES_DIR
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:
if set -q GHOSTTY_RESOURCES_DIR
source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish"
end
Zsh, in ~/.zshrc:
if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
source "$GHOSTTY_RESOURCES_DIR/shell-integration/zsh/ghostty-integration"
fi
Bash, at the top of ~/.bashrc:
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.
Features
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:
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:
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.
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:
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:
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.
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.
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
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:
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.
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:
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
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:
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
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:
ghostty +ssh-cache
ghostty +ssh-cache --remove='[email protected]'
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 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. 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
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:
ghostty -e top
On macOS, from another terminal:
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.
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:
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:
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:
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 collects ready-made ones: 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 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.
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
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:
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.
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
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
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
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
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:
## 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:
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 does, on top of libghostty. Ghostty itself stays a terminal.
Ghostty with tmux
Ghostty has tabs and splits, so do you still need 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.
# ~/.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.
Want me to talk about your product? You can sponsor this site.