Configuration¶
prek reads one configuration file per project. You only need to choose one format:
- prek.toml (TOML) — recommended for new users
- .pre-commit-config.yaml (YAML) — best if you already use pre-commit or rely on tool/editor support
Both formats are first-class and will be supported long-term. They describe the same configuration model: you list repositories under repos, then enable and configure hooks from those repositories.
Pre-commit compatibility¶
prek is fully compatible with pre-commit YAML configs, so your existing .pre-commit-config.yaml files work unchanged.
If you use prek.toml, there’s nothing to worry about from a pre-commit perspective: upstream pre-commit does not read TOML.
If you use the same .pre-commit-config.yaml with both tools, keep in mind:
preksupports several extensions beyond upstreampre-commit.- Upstream
pre-commitmay warn about unknown keys or error out on unsupported features. - To stay maximally portable, avoid the extensions listed below (or keep separate configs).
Notable differences (when using YAML):
- Workspace mode is a
prekfeature that can discover multiple projects; upstreampre-commitis single-project. files/excludecan be written as glob mappings inprek(in addition to regex), which is not supported by upstreampre-commit.repo: builtinadds fast built-in hooks inprek.- Upstream
pre-commitusesminimum_pre_commit_version, whileprekusesminimum_prek_versionand intentionally ignoresminimum_pre_commit_version.
Prek-only extensions¶
These entries are implemented by prek and are not part of the documented upstream pre-commit configuration surface.
They work in both YAML and TOML, but they only matter for compatibility if you share a YAML config with upstream pre-commit.
- Top-level:
- Repo type:
- Hook-level:
Configuration file¶
Location (discovery)¶
By default, prek looks for a configuration file starting from your current working directory and moving upward.
It stops when it finds a config file, or when it hits the git repository boundary.
If you run without --config, prek then enables workspace mode:
- The first config found while traversing upward becomes the workspace root.
- From that root,
preksearches for additional config files in subdirectories (nested projects).
Workspace discovery respects .gitignore, and also supports .prekignore for excluding directories from discovery.
For the full behavior and examples, see Workspace Mode.
Tip
After updating .prekignore, run with --refresh to force a fresh project discovery so the changes are picked up.
If you pass --config / -c, workspace discovery is disabled and only that single config file is used.
File name¶
prek recognizes the following configuration filenames:
prek.toml(TOML).pre-commit-config.yaml(YAML, preferred for pre-commit compatibility).pre-commit-config.yml(YAML, alternate)
In workspace mode, each project uses one of these filenames in its own directory.
One format per repo
We recommend using a single format across the whole repository to avoid confusion.
If multiple configuration files exist in the same directory, prek uses only one and ignores the rest.
The precedence order is:
prek.toml.pre-commit-config.yaml.pre-commit-config.yml
File format¶
Both prek.toml and .pre-commit-config.yaml map to the same configuration model (repositories under repos, then hooks under each repo).
This section focuses on format-specific authoring notes and examples.
TOML (prek.toml)¶
Practical notes:
- Structure is explicit and less indentation-sensitive.
- Inline tables are common for hooks (e.g.
{ id = "ruff" }).
TOML supports both inline tables and array-of-tables, so you can choose between a compact or expanded hook style.
Inline tables (best for small/simple hook configs):
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
rev = "v6.0.0"
hooks = [
{ id = "end-of-file-fixer", args = ["--fix"] },
]
Array-of-tables (more readable for larger hook configs):
[[repos]]
repo = "https://github.com/pre-commit/pre-commit-hooks"
rev = "v6.0.0"
[[repos.hooks]]
id = "trailing-whitespace"
[[repos.hooks]]
id = "check-json"
Example:
YAML (.pre-commit-config.yaml / .yml)¶
Practical notes:
- Regular expressions are provided as YAML strings.
If your regex contains backslashes, quote it (e.g.
files: '\\.rs$'). - YAML anchors/aliases and merge keys are supported, so you can de-duplicate repeated blocks.
Example:
Choosing a format¶
prek.toml
- Clearer structure and less error-prone syntax.
- Recommended for new users or new projects.
.pre-commit-config.yaml
- Long-established in the ecosystem with broad tool/editor support.
- Fully compatible with upstream
pre-commit.
Recommendation
- If you already use
.pre-commit-config.yaml, keep it. - If you want a cleaner, more robust authoring experience, prefer
prek.toml.
Tip
If you want to switch, you can use prek util yaml-to-toml to convert YAML configs to prek.toml.
YAML comments are not preserved during conversion.
Scope (per-project)¶
Each configuration file (prek.toml, .pre-commit-config.yaml, or .pre-commit-config.yml) is scoped to the project directory it lives in.
In workspace mode, prek treats every discovered configuration file as a distinct project:
- A project’s config only controls hook selection and filtering (for example
files/exclude) for that project. - A project may contain nested subprojects (subdirectories with their own config). Those subprojects run using their own configs.
Practical implication: filters in the parent project do not “turn off” a subproject.
Example layout (monorepo with a nested project):
foo/.pre-commit-config.yaml(projectfoo)foo/bar/.pre-commit-config.yaml(projectfoo/bar, nested subproject)
If project foo config contains an exclude that matches bar/**, then hooks for project foo will not run on files under foo/bar:
But if foo/bar is itself a project (has its own config), files under foo/bar are still eligible for hooks when running in the context of project foo/bar.
Excluding a nested project
If foo/bar/.pre-commit-config.yaml exists but you don’t want it to be recognized as a project in workspace mode, exclude it from discovery using .prekignore.
Like .gitignore, .prekignore files can be placed anywhere in the workspace and apply to their directory and all subdirectories.
Tip
After updating .prekignore, run with --refresh to force a fresh project discovery so the changes are picked up.
Validation¶
Use prek validate-config to validate one or more config files.
If you want IDE completion / validation, prek provides a JSON Schema at https://prek.j178.dev/docs/prek.schema.json.
And the schema is also submitted to the JSON Schema Store, so some editors may pick it up automatically.
That schema tracks what prek accepts today, but prek also intentionally tolerates unknown keys for forward compatibility.
Configuration reference¶
This section documents the configuration keys that prek understands.
Top-level keys¶
repos (required)¶
A list of hook repositories.
Each entry is one of:
- a remote repository (typically a git URL)
repo: localfor hooks defined directly in your repositoryrepo: metafor built-in meta hooksrepo: builtinforprek's built-in fast hooks
See Repo entries.
files¶
Global include regex applied before hook-level filtering.
- Type: regex string (default, pre-commit compatible) or a prek-only glob pattern mapping
- Default: no global include filter
This is usually used to narrow down the universe of files in large repositories.
Regex matching
When files / exclude are regex strings, they are matched with search semantics (the pattern can match anywhere in the path).
Use ^ to anchor at the beginning and $ at the end.
prek uses the Rust fancy-regex engine.
Most typical patterns are portable to upstream pre-commit, but very advanced regex features may differ from Python’s re.
prek-only globs
In addition to regex strings, prek supports glob patterns via:
files: { glob: "..." }(single glob)files: { glob: ["...", "..."] }(glob list)
This is a prek extension. Upstream pre-commit expects regex strings here.
For more information on the glob syntax, refer to the globset documentation.
Examples:
exclude¶
Global exclude regex applied before hook-level filtering.
- Type: regex string (default, pre-commit compatible) or a prek-only glob pattern mapping
- Default: no global exclude filter
exclude is useful for generated folders, vendored code, or build outputs.
prek-only globs
Like files, exclude supports glob (single glob or glob list) as a prek extension.
For glob syntax details, see the globset documentation.
Examples:
Verbose regex example (useful for long allow/deny lists):
fail_fast¶
Stop the run after the first failing hook.
- Type: boolean
- Default:
false
This is a global default; individual hooks can also set fail_fast.
default_language_version¶
Map a language name to the default language_version used by hooks of that language.
- Type: map
- Default: none (hooks fall back to
language_version: default)
Example:
prek treats language_version as a version request (often a semver-like selector) and may install toolchains automatically. See Difference from pre-commit.
default_stages¶
Default stages used when a hook does not specify its own.
- Type: list of stage names
- Default: all stages
Allowed values:
manualcommit-msgpost-checkoutpost-commitpost-mergepost-rewritepre-commitpre-merge-commitpre-pushpre-rebaseprepare-commit-msg
default_install_hook_types¶
Default hook type(s) installed by prek install when you don’t pass --hook-type.
- Type: list of git hook types
- Default:
[pre-commit]
This controls which git hook scripts are installed (for example pre-commit vs pre-push).
It is separate from a hook’s stages, which controls when a particular hook is eligible to run.
Allowed values:
pre-commitpre-pushcommit-msgprepare-commit-msgpost-checkoutpost-commitpost-mergepost-rewritepre-merge-commitpre-rebase
minimum_prek_version¶
prek-only
This key is a prek extension. Upstream pre-commit uses minimum_pre_commit_version, which prek intentionally ignores.
Require a minimum prek version for this config.
- Type: string (version)
- Default: unset
If the installed prek is older than the configured minimum, prek exits with an error.
Example:
orphan¶
prek-only
orphan is a prek workspace-mode feature and is not recognized by upstream pre-commit.
Workspace-mode setting to isolate a nested project from parent configs.
- Type: boolean
- Default:
false
When orphan: true, files under this project directory are handled only by this project’s config and are not “seen” by parent projects.
Example:
See Workspace Mode - File Processing Behavior for details.
Repo entries¶
Each item under repos: is a mapping that always contains a repo: key.
Remote repository¶
Use this for hooks distributed in a separate repository.
Required keys:
repo: repository location (commonly an https git URL)rev: version to use (tag, branch, or commit SHA)hooks: list of hook selections
Remote hook definitions live inside the hook repository itself in the
.pre-commit-hooks.yaml manifest (at the repo root). Your config only selects
hooks by id and optionally overrides options. See Authoring Hooks
if you maintain a hook repository.
repo¶
Where to fetch hooks from.
In most configs this is a git URL.
prek also recognizes special values documented separately: local, meta, and builtin.
rev¶
The revision to use for the remote repository.
Use a tag or commit SHA for repeatable results. If you use a moving target (like a branch name), runs may change over time.
hooks¶
The list of hooks to enable from that repository.
Each item must at least specify id.
You can also add hook-level options (filters, args, stages, etc.) to customize behavior.
Example:
Notes:
- For reproducibility, prefer immutable pins (tags or commit SHAs).
prek auto-updatecan help updaterevvalues.
repo: local¶
Define hooks inline inside your repository.
Keys:
repo: must belocalhooks: list of local hook definitions (see Local hook definition)
Example:
repo: meta¶
Use pre-commit-style meta hooks that validate and debug your configuration.
prek supports the following meta hook ids:
check-hooks-applycheck-useless-excludesidentity
Restrictions:
idis required.entryis not allowed.language(if set) must besystem.
You may still configure normal hook options such as files, exclude, stages, etc.
Example:
repo: builtin¶
prek-only
repo: builtin is specific to prek and is not compatible with upstream pre-commit.
Use prek’s built-in fast hooks (offline, zero setup).
Restrictions:
idis required.entryis not allowed.language(if set) must besystem.
Example:
For the list of available built-in hooks and the “automatic fast path” behavior, see Built-in Fast Hooks.
Hook entries¶
Hook items under repos[*].hooks have slightly different shapes depending on the repo type.
Remote hook selection¶
For a remote repo, the hook entry must include:
id(required): selects the hook from the repository
All other hook keys are optional overrides (for example args, files, exclude, stages, …).
Advanced overrides
prek also supports overriding name, entry, and language for remote hooks.
This can be useful for experimentation, but it may reduce portability to the original pre-commit.
Local hook definition¶
For repo: local, the hook entry is a full definition and must include:
id(required): stable identifier used byprek run <id>and selectorsname(required): label shown in outputentry(required): command to executelanguage(required): howpreksets up and runs the hook
Builtin/meta hook selection¶
For repo: builtin and repo: meta, the hook entry must include id.
You can optionally provide name and normal hook options (filters, stages, etc), but not entry.
Common hook options¶
These keys can appear on hooks (remote/local/builtin/meta), subject to the restrictions above.
id¶
The stable identifier of the hook.
- For remote hooks, this must match a hook id defined by the remote repository.
- For local hooks, you choose it.
id is also used for CLI selection (for example prek run <id> and PREK_SKIP).
name¶
Human-friendly label shown in output.
- Required for
repo: localhooks. - Optional as an override for remote/meta/builtin hooks.
entry¶
The command line to execute for the hook.
- Required for
repo: localhooks. - Optional override for remote hooks.
- Not allowed for
repo: metaandrepo: builtin.
If pass_filenames: true, prek appends matching filenames to this command when running.
language¶
How prek should run the hook (and whether it should create a managed environment).
- Required for
repo: localhooks. - Optional override for remote hooks.
- Not allowed (except as
system) forrepo: metaandrepo: builtin.
Common values include system, python, node, rust, golang, ruby, and docker.
See Language Support for per-language behavior, supported values, and language_version details.
Language name aliases
For compatibility with upstream pre-commit, the following legacy language names are also accepted:
unsupportedis treated assystemunsupported_scriptis treated asscript
alias¶
An alternate identifier for selecting the hook from the CLI.
If set, you can run the hook via either prek run <id> or prek run <alias>.
args¶
Extra arguments appended to the hook’s entry.
- Type: list of strings
Example:
env¶
prek-only
env is a prek extension and may not be recognized by upstream pre-commit.
Extra environment variables for the hook process.
- Type: map of string to string
Values override the existing process environment (including variables such as PATH).
For docker / docker_image hooks, these variables are passed into the container rather than being applied to the container runtime command.
Example:
files / exclude¶
Filters applied to candidate filenames.
filesselects which files are eligible for the hook.excluderemoves files matched byfiles.
If you use both global and hook-level filters, the effective behavior is “global filter first, then hook filter”.
By default (and for compatibility with upstream pre-commit), these are regex strings.
As a prek extension, you can also specify globs using glob or a glob list.
See Top-level files and Top-level exclude for syntax notes and examples.
types / types_or / exclude_types¶
File-type filters based on identify tags.
Tip
Use prek util identify <path> to see how prek tags a file when you’re troubleshooting types filters.
Compared to regex-only filtering (files / exclude), tag-based filtering is often easier and more robust:
- tags can match by file extension and by shebang (for extensionless scripts)
- you can easily exclude things like symlinks or binary files
Common tags include:
-
file,text,binary,symlink,executable -
language-ish tags such as
python,rust,javascript,yaml,toml, ... -
types: all listed tags must match (logical AND) -
types_or: at least one listed tag must match (logical OR) -
exclude_types: tags that disqualify a file
How these combine:
files/exclude,types, andtypes_orare combined with AND.- Tags within
typesare combined with AND. - Tags within
types_orare combined with OR.
Defaults:
types:[file](matches all files)types_or:[]exclude_types:[]
These filters are applied in addition to regex filtering.
Examples:
[[repos]]
repo = "local"
hooks = [
# AND: must be under `src/` AND have the `python` tag
{
id = "lint-py",
name = "Lint (py)",
language = "system",
entry = "python -m ruff check",
files = "^src/",
types = ["python"],
exclude_types = ["symlink"]
},
# OR: match any of the listed tags under `web/`
{
id = "lint-web",
name = "Lint (web)",
language = "system",
entry = "npm run lint",
files = "^web/",
types_or = ["javascript", "jsx", "ts", "tsx"]
},
]
If you need to match a path pattern that doesn’t align with a hook’s default types (common when reusing an existing hook in a nonstandard way), override it back to “all files” and use files:
always_run¶
Run the hook even when no files match.
- Type: boolean
- Default:
false
This is commonly used for hooks that check repository-wide state (for example, running a test suite) rather than operating on specific files.
pass_filenames¶
Controls whether prek appends the matching filenames to the command line.
- Type: boolean
- Default:
true
Set pass_filenames: false for hooks that don’t accept file arguments (or that discover files themselves).
stages¶
Declare which stages a hook is eligible to run in.
- Type: list of stage names
- Default: all stages
Allowed values:
manualcommit-msgpost-checkoutpost-commitpost-mergepost-rewritepre-commitpre-merge-commitpre-pushpre-rebaseprepare-commit-msg
When you run prek run --hook-stage <stage>, only hooks configured for that stage are considered.
require_serial¶
Force a hook to run without parallel invocations (one in-flight process for that hook at a time).
- Type: boolean
- Default:
false
This is useful for tools that use global caches/locks or otherwise can’t handle concurrent execution.
priority¶
prek-only
priority controls prek's scheduler and does not exist in upstream pre-commit.
Each hook can set an explicit priority (a non-negative integer) that controls when it runs and with which hooks it may execute in parallel.
Scope:
priorityis evaluated within a single configuration file and is compared across all hooks in that file, even if they appear under differentrepos:entries.prioritydoes not coordinate across different config files. In workspace mode, each project’s config file is scheduled independently.
Hooks run in ascending priority order: lower priority values run earlier. Hooks that share the same priority value run concurrently, subject to the global concurrency limit.
When priority is omitted, prek assigns an implicit value based on hook order to preserve sequential behavior.
Example:
[[repos]]
repo = "local"
hooks = [
{
id = "format",
name = "Format",
language = "system",
entry = "python3 -m ruff format",
always_run = true,
priority = 0,
},
{
id = "lint",
name = "Lint",
language = "system",
entry = "python3 -m ruff check",
always_run = true,
priority = 10,
},
{
id = "tests",
name = "Tests",
language = "system",
entry = "just test",
always_run = true,
priority = 20,
},
]
repos:
- repo: local
hooks:
- id: format
name: Format
language: system
entry: python3 -m ruff format
always_run: true
priority: 0
- id: lint
name: Lint
language: system
entry: python3 -m ruff check
always_run: true
priority: 10
- id: tests
name: Tests
language: system
entry: just test
always_run: true
priority: 20
Parallel hooks modifying files
If two hooks run in the same priority group and both mutate the same files (or depend on shared state), results are undefined. Use separate priorities to avoid overlap.
require_serial is different
require_serial: true prevents concurrent invocations of the same hook.
It does not prevent other hooks from running alongside it; use a unique priority if you need exclusivity.
fail_fast¶
Hook-level fail-fast behavior.
- Type: boolean
- Default:
false
If true, a failure in this hook stops the run immediately.
verbose¶
Print hook output even when the hook succeeds.
- Type: boolean
- Default:
false
log_file¶
Write hook output to a file when the hook fails (and also when verbose: true).
- Type: string path
description¶
Free-form description shown in listings / metadata.
- Type: string
language_version¶
Choose the language/toolchain version request for this hook.
- Type: string
- Default:
default
If not set, prek may use default_language_version for the hook’s language.
prek-only
language_version is treated as a version request, not a single pinned value. For languages that use semver requests, you can specify ranges (for example ^1.2, >=1.5, <2.0).
Special values:
default: use the language’s default resolution logic.system: require a system-installed toolchain (no downloads).
Language-specific behavior:
- Python: passed to the Python resolver (for example
python3,python3.12, or a specific interpreter name). May trigger toolchain download. - Node: passed to the Node resolver (for example
20,18.19.0). May trigger toolchain download. - Go: uses Go version strings such as
1.22.1(downloaded if missing). - Rust: supports rustup toolchains such as
stable,beta,nightly, or versioned toolchains. - Other languages: parsed as a semver request and matched against the installed toolchain version.
Examples:
additional_dependencies¶
Extra dependencies for hooks that run inside a managed environment (for example Python or Node hooks).
- Type: list of strings
If you set this for a language that doesn’t support dependency installation, prek fails with a configuration error.
minimum_prek_version¶
prek-only
This is a prek-specific requirement gate. Upstream pre-commit does not have a hook-level minimum version key.
Require a minimum prek version for this specific hook.
- Type: string (version)
- Default: unset
Environment variables¶
prek supports the following environment variables:
-
PREK_HOME— Override the prek data directory (caches, toolchains, hook envs). If beginning with~, it is expanded to the user’s home directory. Defaults to~/.cache/prekon macOS and Linux, and%LOCALAPPDATA%\prekon Windows. -
PREK_COLOR— Control colored output: auto (default), always, or never. -
PREK_QUIET— Control quiet output mode. Set to1for quiet mode (equivalent to-q, only shows failed hooks), or2for silent mode (equivalent to-qq, no output to stdout). -
PREK_SKIP— Comma-separated list of hook IDs to skip (e.g. black,ruff). See Skipping Projects or Hooks for details. -
PREK_ALLOW_NO_CONFIG— Allow running without a configuration file (useful for ad‑hoc runs). -
PREK_NO_CONCURRENCY— Disable parallelism for installs and runs (setPREK_NO_CONCURRENCY=1to force concurrency to1). -
PREK_NO_FAST_PATH— Disable Rust-native built-in hooks; always use the original hook implementation. See Built-in Fast Hooks for details. -
PREK_UV_SOURCE— Control how uv (Python package installer) is installed. Options:github(download from GitHub releases)pypi(install from PyPI)tuna(use Tsinghua University mirror)aliyun(use Alibaba Cloud mirror)tencent(use Tencent Cloud mirror)pip(install via pip)- a custom PyPI mirror URL
If not set, prek automatically selects the best available source.
-
PREK_NATIVE_TLS— Use the system trusted store instead of the bundledwebpki-rootscrate. -
PREK_CONTAINER_RUNTIME— Specify the container runtime to use for container-based hooks (e.g.,docker,docker_image). Options:auto(default, auto-detect available runtime)dockerpodmancontainer(Apple's Container runtime on macOS, see container)
Compatibility fallbacks:
PRE_COMMIT_ALLOW_NO_CONFIG— Fallback forPREK_ALLOW_NO_CONFIG.PRE_COMMIT_NO_CONCURRENCY— Fallback forPREK_NO_CONCURRENCY.SKIP— Fallback forPREK_SKIP.