FreeToGenerate.com

The same two lines mean different things in a .dockerignore and a .gitignore. This shows both verdicts at once. Nothing is uploaded.

Try one:

One pattern per line. A hash starts a comment only in the first column, and an exclamation mark makes an exception to an earlier line.

Only git's rules care: a pattern ending in a slash applies to directories alone. Docker has no such pattern, because the slash is erased before matching.

What each file would do with this path

.dockerignore

Sent to the builder

Decided by line 2, !util/docker/webmatched on util/docker/web

.gitignore

Ignored

Decided by line 1, **matched on util

These two files disagree about this path. The same lines in a .dockerignore and a .gitignore do not mean the same thing, and nothing warns you — the build simply sends more, or less, than you expected.

How each line was read

LineAs writtenAfter cleaningMatched as
1****suffix
2!util/docker/webutil/docker/webexceptionexact string

Everything here runs in your browser. Nothing you paste is uploaded.

Also available in: Español · Português · Français · العربية

Dockerignore generator and checker: it is not a .gitignore

Write a .dockerignore, check any path against it, and see side by side what git would have done with the identical file.

What is a .dockerignore file?

A .dockerignore sits next to your Dockerfile and lists what should not be sent to the builder. Everything in the build context is packed up and handed over before the first instruction runs, so a stray node_modules or .git directory costs upload time on every build, lands in the image if anything copies it, and quietly breaks layer caching because the context changes whenever those files do.

It looks exactly like a .gitignore. One pattern per line, a hash for comments, an exclamation mark to make an exception. That resemblance is the problem: the two files are matched by completely different code, and the same lines can give opposite answers. Nothing warns you, because both files are valid in both places.

This page runs both engines at once. Write your .dockerignore, name a path, and it shows what Docker's matcher decides, what git's rules decide, which line was responsible in each case, and — the part that explains everything — which path each of them actually matched.

How to use it

  1. Paste or write the file. Each line is shown below with the form it takes after cleaning and how it will be matched, whether as an exact string, a prefix, a suffix or a pattern. The sample buttons load the four cases where the two files disagree, plus one where they agree.
  2. Name a path. Relative to the build context, using forward slashes. Tick the directory box if it is a folder; only git's rules care about that, and the tool explains why.
  3. Read both verdicts. Each panel names the deciding line and the path it matched on. When they disagree, that is a real difference in behaviour and not a display quirk.

The four differences that catch people

The first is the one nobody expects, and Docker's own test suite asserts it. Exclude everything with a double star, then re-include a directory with an exclamation mark, and a file inside that directory is sent to the builder. Give git the same two lines and the file stays ignored, because git walks the path from the top down and never looks inside a directory it has already excluded. Docker's matcher keeps evaluating patterns after an ancestor matches, so the later exception still applies. Same two lines, opposite results.

The second costs the most build time. A pattern with no wildcard in it is compared to the path by plain string equality, and to each parent directory in turn — nothing else. So a line reading node_modules excludes the one at the root of your context and no other. The copy nested inside a package directory is packed up and sent, every build. In a .gitignore that same line matches at every depth, which is why the file looks like it is working. Write two stars and a slash in front of it and Docker reaches the nested ones too.

The third is a character that vanishes. Every pattern is run through Go's path-cleaning function before it is used, and that function strips a trailing slash. So a line ending in a slash is not directory-only here — it is the same pattern without the slash, and it will match a file of that name just as happily. In git the trailing slash is precisely what restricts a pattern to directories.

The fourth is a character that means nothing. A leading slash is stripped when the file is read, and Docker's own code comments say the two spellings are equivalent. In git the leading slash anchors a pattern to the directory the file sits in, and leaving it off is what makes a pattern match at any depth. The same character, load-bearing on one side and inert on the other.

Honest limitations

This is matching, not building. It tells you whether a path would be sent to the builder; it does not know whether the file exists, how large your context really is, or what your Dockerfile does with any of it. The Dockerfile and the .dockerignore themselves are always sent regardless of what the patterns say, because the builder needs them.

Paths are treated as forward-slash separated, which is what the Linux builder works with. The behaviour reproduced here is that of Docker's own matcher, which is worth stating plainly: there is no specification for this format. The matcher is the definition, and the documentation is not quite a description of it — the docs name a Go standard-library function whose pattern rules have no recursive wildcard at all, while the implementation compiles each pattern to a regular expression and supports one. Twenty-three of the reference implementation's own test cases depend on that behaviour.

One rule that looks like a difference is not. Docker passes square brackets straight into a regular expression, so a character class negates with a caret rather than the exclamation mark a shell glob would use — but git accepts the caret too, so both spellings behave the same on both sides. It is mentioned here because it is the obvious thing to assume, and assuming it would be wrong.

Finally, a comment only counts as a comment when the hash is the very first character of the line. Indent it by a single space and it stops being a comment and becomes a pattern matching a file of that name. That ordering comes straight from the reference implementation, which tests for the hash before it trims whitespace.

Why is it free?

Matching a path against a list of patterns is a few hundred lines of string handling, and your browser does it as you type. No server sees your file, so there is nothing to meter and no account to create.

Nothing is uploaded. Whatever you paste stays in this tab.