Electronics Design AU
KiCad

How Do You Write Custom Design Rules (.kicad_dru) in KiCad?

Last updated 19 August 2026 · 11 min read

Direct Answer

KiCad custom design rules are written in a .kicad_dru text file using an s-expression syntax: each (rule "name" ...) block combines an optional condition expression that selects which objects it applies to, an optional layer restriction, and a constraint clause — clearance, courtyard_clearance, silk_clearance, diff_pair_gap, track_width, and dozens more — that defines the actual check. Rules are typed into Board Setup → Design Rules → Custom Rules, validated with the Check Rule Syntax button, and evaluated in reverse file order: the last rule in the file is checked first, and the first matching rule wins.

Detailed Explanation

Board Setup's Design Rules panel (clearance, track width, via size, net classes) covers most boards adequately, and importing a fabricator's published .kicad_dru file (covered in How to Use KiCad: Schematic Entry and PCB Layout Workflow and How to Export Gerber Files from KiCad for PCB Fabrication) extends that with a fab's specific process floor. Neither covers a requirement that only applies to part of a board: a wider clearance around a mains-adjacent net, a tighter courtyard on one connector family, or a differential-pair gap that only matters on a handful of specific nets. That's what KiCad's custom rules language is for. This page assumes you're already comfortable with what DRC is and why it matters. See What Are PCB Design Rules (DRC)? if you need that background first. For the topic overview, see the KiCad topic.

Custom rules were introduced in KiCad 6.0 (December 2021) and have picked up additional constraint types in every release since; this page reflects the rule language as documented for KiCad 9. Board Setup's custom rules editor (Board Setup → Design Rules → Custom Rules) provides context-sensitive autocomplete for valid keywords and properties, which is worth leaning on directly rather than memorising the full grammar.

The .kicad_dru Rule Grammar

Every rule set is written in an s-expression syntax and stored in a <project>.kicad_dru file in the project directory once you close Board Setup. The file must start with a version header, followed by one or more rule blocks:

(version 1)

(rule <name>
    (severity <severity>)
    (layer <layer_name>)
    (condition <expression>)
    (constraint <constraint_type> <constraint_arguments>))

severity, layer, and condition are all optional: a rule with none of them applies unconditionally, everywhere, to every object DRC checks. constraint is the only mandatory clause; without one, the rule does nothing. Comments start with #, string literals take single or double quotes, and dimensions take a unit suffix: mm, mil (or th), in (or "), deg, or rad. A bare number with no suffix uses the board's currently configured default unit, which is an easy mistake to introduce silently. Always write the suffix explicitly.

Severity, Layers, and the Custom Rules Editor

severity is one of error, warning, ignore, or exclusion. error and warning behave like KiCad's built-in violations, differing only in how they're flagged in the DRC report; ignore suppresses the check's output for matched objects without deleting the underlying constraint, and exclusion behaves similarly but is intended for a reviewed, deliberate suppression rather than a permanently disabled check.

layer restricts a rule to a specific copper or non-copper layer (F.Cu, B.SilkS, and so on), or to the special values outer (front and back copper) and inner (all internal copper layers). Restricting by layer is more efficient than writing an equivalent condition that tests the layer property, and KiCad's own documentation notes this explicitly. Prefer the layer clause when a rule is layer-scoped rather than reaching for a condition expression first.

Once rules are typed into the Custom Rules text box, click Check Rule Syntax before closing Board Setup. It confirms the rules parse; it does not confirm they match anything in your board, which only shows up in the DRC report after you run a check.

Rule Evaluation Order: The Most Important Gotcha

KiCad evaluates rules in reverse file order (the last rule written in the file is checked first) and stops at the first rule whose condition matches. This single behaviour causes more confused "why isn't my rule working" reports than any other part of the custom rules language, because it's the opposite of what most people assume from reading top to bottom.

Consider a board-wide clearance floor plus a tighter rule for mains-adjacent nets, written in this order:

(version 1)

(rule "board_default_clearance"
    (constraint clearance (min 0.2mm)))

(rule "mains_clearance"
    (condition "A.hasNetclass('MAINS')")
    (constraint clearance (min 3mm)))

This works correctly: mains_clearance is last in the file, so it's checked first, and its condition only matches objects on the MAINS net class; everything else falls through to board_default_clearance.

Now suppose someone later appends a new default rule at the bottom of the same file, a common, easy edit to make without thinking about ordering:

(version 1)

(rule "mains_clearance"
    (condition "A.hasNetclass('MAINS')")
    (constraint clearance (min 3mm)))

(rule "board_default_clearance"
    (constraint clearance (min 0.2mm)))

board_default_clearance is now last in the file, so it's checked first, and because it has no condition clause, it matches every object unconditionally, including mains-adjacent nets. mains_clearance never gets evaluated. DRC will still pass; it just silently stops checking for the 3 mm mains clearance and falls back to the 0.2 mm generic floor instead, with no error or warning to indicate the rule stopped working. The practical guidance: write your most specific, overriding rules physically after (further down the file than) your general, catch-all rules, and re-verify with the DRC report (not just Check Rule Syntax) any time you reorder or append to an existing rule set.

Constraint Types

The constraint clause names what's actually being checked. KiCad groups roughly four categories worth knowing, each taking (min x), (opt x), and/or (max x) value arguments depending on the constraint:

Electrical and spacing: clearance, hole_clearance, edge_clearance, courtyard_clearance, silk_clearance, hole_to_hole, creepage, bridged_mask

Manufacturability: hole_size, solder_mask_expansion, solder_paste_abs_margin, solder_paste_rel_margin, text_height, text_thickness, thermal_relief_gap, thermal_spoke_width

High-speed and routing: track_width, annular_width, via_diameter, via_count, diff_pair_gap, diff_pair_uncoupled, length, skew, connection_width

Policy and structural: disallow, physical_clearance, physical_hole_clearance, track_angle, track_segment_length, via_dangling, zone_connection, assertion

A few are worth quoting directly from KiCad's own descriptions. diff_pair_gap "checks the gap between parallel tracks in a differential pair" (its opt value is what the interactive router targets when placing a new pair). diff_pair_uncoupled "checks the distance that a differential pair track is routed uncoupled from the other polarity track" (the fan-out stub length near a connector or via). courtyard_clearance "checks the clearance between footprint courtyards." hole_to_hole measures "between the diameters of the holes, not between their centers." These wording details matter when deciding which constraint actually expresses the check you want.

Worked Examples

High-Voltage Clearance by Net Class

Net classes (Board Setup → Design Rules → Net Classes) group nets logically; hasNetclass() in a condition lets a rule target that group directly rather than naming individual nets. The rule below assumes copper on the primary (mains) side of an isolation barrier needs more clearance than the board's general 0.2mm minimum, with "MAINS" already assigned as a net class in Board Setup → Design Rules → Net Classes:

(version 1)

(rule "mains_clearance"
    (condition "A.hasNetclass('MAINS')")
    (constraint clearance (min 3mm)))

Treat the 3 mm figure here as illustrative syntax, not a safety value to copy. The actual creepage and clearance a mains-adjacent net needs depends on working voltage, pollution degree, and material group, and comes from a safety standard: IEC 62368-1 (or IEC 60950-1 for legacy designs) creepage/clearance tables, not a rule of thumb. Derive the number for your specific design first, then encode it.

Differential Pair Gap by Net Class

inDiffPair() matches objects that belong to a differential pair whose name matches a pattern (wildcards supported), and combining it with hasNetclass() scopes a rule to a specific signal group rather than every differential pair on the board:

(version 1)

(rule "usb_diffpair_gap"
    (condition "A.hasNetclass('USB_DP') && A.inDiffPair('USB*')")
    (constraint diff_pair_gap (min 0.15mm) (opt 0.2mm) (max 0.25mm)))

(rule "usb_diffpair_uncoupled"
    (condition "A.hasNetclass('USB_DP') && A.inDiffPair('USB*')")
    (constraint diff_pair_uncoupled (max 3mm)))

The actual min/opt/max gap and the uncoupled-length ceiling should come from your target impedance and the USB specification's skew tolerance, not from the numbers above. See How Do You Route Controlled-Impedance and Differential Pairs in KiCad? for computing the correct gap from your stack-up with the PCB Calculator before writing the rule that enforces it.

Courtyard and Silkscreen Rules

memberOfFootprint() matches by reference designator pattern, which is useful for tightening a check on one connector or IC family without touching the board-wide default, and the layer clause is the efficient way to scope a rule to silkscreen specifically. The first rule below tightens courtyard spacing around fine-pitch ICs (U1, U2, ...) past the board's general courtyard_clearance default; the second widens silkscreen-to-copper clearance on the front silk layer:

(version 1)

(rule "fine_pitch_courtyard"
    (condition "A.memberOfFootprint('U*')")
    (constraint courtyard_clearance (min 0.5mm)))

(rule "front_silk_clearance"
    (layer F.SilkS)
    (constraint silk_clearance (min 0.2mm)))

Layer-Specific Clearance Override

Outer copper layers often tolerate looser clearance than a dense inner layer, or vice versa depending on your stack-up and process. KiCad's own documentation uses this exact pattern:

(version 1)

(rule "clearance_outer"
    (layer outer)
    (constraint clearance (min 0.25mm)))

Note the comment in KiCad's example is worth repeating: inner-layer clearance in this case is left to the board's general minimum clearance setting in Board Setup, not restated in a second rule. A custom rule only needs to express the exception, not duplicate the base configuration.

Common Mistakes

  • Forgetting (version 1). Every .kicad_dru file needs the version header as its first line; without it the rule set fails to parse.
  • Misreading rule evaluation order. As covered above, this is the single most common source of a rule that silently never fires. When a rule stops working after an edit, check whether something was added or reordered relative to it in the file before assuming the condition itself is wrong.
  • A condition that never matches. A misspelled property name, wrong-case net class string, or a net class that was renamed in Board Setup after the rule was written all produce a rule that parses cleanly but matches nothing. Confirm a rule actually fired by checking the DRC violations report against a board region you know should trigger it, not just by running Check Rule Syntax.
  • Conflating disallow with severity ignore/exclusion. constraint disallow is a hard block: it flags the matched object type as not permitted at all. Setting severity to ignore or exclusion suppresses reporting for a check that would otherwise run. These solve different problems and are not interchangeable.
  • Dropping the unit suffix. A bare number takes the board's default display unit, which may not be what you intended if you're used to typing mm but the project is set to inches, or vice versa. Write mm, mil, or in explicitly on every dimension.

Custom Rule or DRC Exclusion?

Both suppress or add checks outside the base Board Setup constraints, but they solve different problems and aren't interchangeable:

  • Write a custom rule when the constraint reflects a real, repeatable requirement that should apply everywhere it's relevant, now and on future revisions: mains clearance by net class, a differential-pair tolerance, a fab's specific process floor for one layer, a courtyard policy for a connector family. A rule is proactive: it catches every future instance of the same condition, and it travels with the .kicad_dru file if reused on another project.
  • Use a DRC exclusion for a genuinely one-off, already-reviewed exception at a specific location: a particular pad-to-pad clearance that's acceptable for documented reasons but would otherwise flag, and that you don't want (or need) to generalise into a standing rule. An exclusion is tied to that one violation instance; it does nothing to catch the same situation if it recurs elsewhere on the board.

A practical signal for which one you need: if you find yourself excluding the same category of violation more than once or twice on a board, that's usually a sign the underlying requirement should be a custom rule instead, not a growing list of individual exclusions.

Design Considerations

  • Custom rules stack on top of Board Setup's base constraints, not instead of them. The general clearance, track-width, and net-class minimums still apply everywhere a custom rule doesn't explicitly override them with a matching condition and a looser value.
  • Write general rules first in the file, specific overrides later. Given reverse evaluation order, this reads naturally top-to-bottom as "general case, then exceptions" even though KiCad checks it bottom-to-top internally.
  • Prefer layer clauses over equivalent condition expressions when a rule is purely layer-scoped. It's the pattern KiCad's own examples use, and it's more efficient to evaluate.
  • Re-run DRC after any reordering, not just Check Rule Syntax, since syntax validation can't tell you whether a rule actually matches anything or got shadowed by another rule's evaluation order.
  • Keep the rule set in version control alongside the rest of the project. .kicad_dru is plain text like every other KiCad project file, so changes to your custom rules diff and review the same way a schematic or layout change does.

Setting up a rule set that correctly reflects both a fabricator's real process capability and a design's own electrical requirements, rather than leaving clearance and diff-pair tolerances at generic defaults, is part of a professional layout process; Zeus Design's PCB design team establishes and validates this at the start of every project.

Frequently Asked Questions

What's the difference between a custom rule and a DRC exclusion in KiCad?
A DRC exclusion suppresses one specific, already-flagged violation at its exact location — it's a one-off acknowledgement that a particular instance is acceptable, and it does nothing to catch the same mistake if it recurs elsewhere on the board. A custom rule is a standing, board-wide (or net-class, layer, or footprint-scoped) policy that actively checks for a class of violations everywhere it applies, and it travels with the .kicad_dru file if you reuse it on a future project. Use a custom rule when the constraint reflects a real, repeatable requirement — mains clearance, differential-pair tolerance, a fab's process floor. Use an exclusion for a genuinely one-off, reviewed exception that doesn't generalise.
Why isn't my custom rule firing even though 'Check Rule Syntax' shows no errors?
Syntax checking only confirms the rule parses; it doesn't confirm the rule actually matches anything. The most common cause is rule evaluation order: KiCad checks rules in reverse file order and stops at the first match, so an earlier-added, condition-less catch-all rule placed later in the file can silently intercept every object before your more specific rule further up ever gets a chance to run. The next most common cause is a condition that references the wrong property name or an unmatched net class string — re-run DRC and check the violations report to confirm which rule, if any, actually fired against the objects you expected.
Can a custom rule make a check less strict than the board's default constraints?
Yes, within limits. A rule with a matching condition and a looser constraint value can relax a check for a specific subset of objects, provided it's positioned so it's evaluated before (i.e. later in the file than) any broader rule that would otherwise apply first. Setting severity to ignore or exclusion suppresses a check's reporting entirely for the matched objects rather than loosening its numeric value. Custom rules add checks on top of Board Setup's base constraints and net class settings — they don't lower the board's overall clearance or track-width minimums unless a rule explicitly targets that same check with a matching condition.
Do .kicad_dru custom rules get included when I share or archive a KiCad project?
Yes. The custom rules editor writes them to a plain-text <project>.kicad_dru file in the project directory, alongside .kicad_pro, .kicad_sch, and .kicad_pcb, so it version-controls and archives with the rest of the project. This is a separate file from a fabricator's own published .kicad_dru — see How to Use KiCad: Schematic Entry and PCB Layout Workflow for importing a fab's rule file. If you write custom rules that encode your fab's confirmed capability, keep both: import theirs for the manufacturing floor, and add your own rules for anything project-specific it doesn't cover.

References

Related Questions

Related Forum Discussions