| | |

YAML 05 ๐Ÿ“„ Comments and Multi-Document Streams (— and …)

โฑ๏ธ Estimated read: 10 min | |

YAML comments and document markers serve two different purposes. A comment is text the parser ignores; it exists for the human reading the file. A document marker separates one YAML document from the next within a single stream, which is how a file can contain multiple independent configurations without being split into separate files. Both features are small, but both have rules that are easy to get wrong: a # inside a quoted string is not a comment, and a --- inside a block scalar is not a document separator.

This chapter covers the comment syntax, the quoting rule that disables it, the document start marker ---, the document end marker ..., and the multi-document stream pattern that tools like Kubernetes use to bundle related resources. The sections that follow walk through comment placement and the quoted-string exception, the two document markers and their asymmetry, and the multi-document stream behavior that determines how parsers expose each document.

Key point: A # starts a comment only outside quoted scalars, and --- starts a new document while ... ends the current one; both markers must appear at the start of a line with nothing else on it.


Why comments and document streams exist

The documentation problem. YAML is a configuration language, and configuration needs explanation. A replicas: 3 value needs a comment saying why three, and a disabled block needs a comment saying it is disabled. YAML’s comment syntax is simpleโ€”a # followed by textโ€”but the rule that a # inside a quoted string is not a comment is the one that trips people who write values containing the character . A URL fragment like https://example.com/#section must be quoted, or the parser reads #section as a comment and truncates the value.

The bundling problem. A Kubernetes deployment typically needs a Deployment, a Service, and a ConfigMap. Each is a separate YAML document with its own apiVersion, kind, and metadata. Writing them in one file with --- between them keeps related resources together and lets a single kubectl apply -f command process all of them . Without document markers, the file would be one document with three top-level keys, which is not the structure Kubernetes expects.

The stream semantics problem. A YAML file is not one document; it is a stream of zero or more documents. The --- marker starts a document, and ... ends one . The first document can begin without a marker, and the stream ends at end-of-file. Parsers expose the stream differently: some return only the first document, some return an array, and some require explicit iteration . Knowing which behavior a parser implements is necessary before relying on multi-document files.


a. Comments and the quoted-string exception

A comment begins with # and continues to the end of the line. It can appear on its own line or after a value. Everything from the # onward is ignored by the parser .

# A full-line comment
server:
  host: localhost   # an inline comment after a value
  port: 8080        # comments can align for readability

The exception is quoting. A # inside a single-quoted or double-quoted scalar is part of the string, not a comment . This is how a value that contains the character is preserved.

# The hash is part of the value because the scalar is quoted
url: "https://example.com/page#section"
message: 'Use the # symbol for tags'

Unquoted values containing # are truncated. A plain scalar stops at the space before the hash, and the rest becomes a comment.

# INVALID INTENT: the value is "https://example.com/page", not the fragment
url: https://example.com/page#section

Comments cannot appear inside a block scalar. The # in a literal or folded block is content, not a comment .

script: |
  # this is part of the script, not a YAML comment
  echo "hello"

b. Document start and end markers

The document start marker is ---. It must appear at the beginning of a line, optionally preceded by whitespace, and nothing else can follow it on that line except a comment . A file can begin with --- or without it; the marker is required only when a second document follows the first.

# Document 1 begins here (marker optional for the first document)
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config

---
# Document 2 begins after the marker
apiVersion: v1
kind: Secret
metadata:
  name: app-secret

The document end marker is .... It terminates the current document and is used less often than ---. When a stream ends with ..., any content after it is either a new document (if --- follows) or invalid .

# Document ends explicitly
apiVersion: v1
kind: Service
metadata:
  name: web
...

The asymmetry matters: --- is common because bundling multiple resources is common; ... is rare because end-of-file serves as an implicit end marker. The ... form appears when a stream needs to signal that the document is complete before the file ends, such as when appending to a file or when a parser needs an explicit terminator.


c. Multi-document streams and parser behavior

A multi-document file is a stream of documents separated by ---. The parser returns each document as an independent root node; the documents do not share anchors, aliases, or merge keys .

# A three-document stream
---
name: first
value: 1
---
name: second
value: 2
---
name: third
value: 3

The same YAML can be parsed as an array of three objects, but each object is a separate document. Anchors defined in one document are not visible in the next.

# INVALID ACROSS DOCUMENTS: the anchor does not carry over
---
defaults: &d
  timeout: 30
---
service:
  <<: *d   # ERROR: unknown anchor in this document

Tools that consume multi-document YAML expect this behavior. Kubernetes applies each document independently, so a Deployment and its Service can reference each other by name but cannot share YAML anchors . The separation is the point: each document is a complete unit that can be validated, applied, or deleted on its own.


Complete Example Session

# ============================================
# PART 1: FULL-LINE AND INLINE COMMENTS
# ============================================
# Application configuration
server:
  host: localhost      # bind address
  port: 8080           # http port

# ============================================
# PART 2: THE QUOTED-HASH EXCEPTION
# ============================================
docs:
  base_url: "https://example.com/docs#intro"   # hash preserved
  tag: 'release#1'                             # hash preserved
  # unquoted_hash: value#comment              # would truncate

# ============================================
# PART 3: BLOCK SCALAR WITH HASH (CONTENT, NOT COMMENT)
# ============================================
script: |
  #!/bin/bash
  echo "start"   # this is inside the script

# ============================================
# PART 4: MULTI-DOCUMENT STREAM
# ============================================
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: info
---
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  password: c2VjcmV0
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 3

# ============================================
# PART 5: EXPLICIT END MARKER
# ============================================
...
# Content after ... without --- is a parse error
# The ... marker signals the stream is complete

# ============================================
# PART 6: THE PARSED MENTAL MODEL
# ============================================
# Stream: [Document, Document, Document]
# Document 1: { apiVersion: "v1", kind: "ConfigMap", ... }
# Document 2: { apiVersion: "v1", kind: "Secret", ... }
# Document 3: { apiVersion: "apps/v1", kind: "Deployment", ... }
#
# Each document is independent. Anchors do not cross boundaries.

The parts covered comment placement, the quoted-hash exception, hash as block scalar content, a three-document Kubernetes-style stream, the explicit end marker, and the parsed stream model.


Quick Reference

Comment Rules

RuleDetail
Comment start# outside quotes
Full-line# comment at any indentation
Inlinevalue # comment
Inside quotesNot a comment
Inside block scalarContent, not comment
Tabs in commentsAllowed

Document Markers

MarkerNameEffect
---Document startBegins a new document
...Document endEnds the current document
None (first)Implicit startFirst document begins at file start
None (end)Implicit endStream ends at EOF

Multi-Document Behavior

AspectBehavior
Document independenceEach is a separate root
Anchors across documentsNot visible
Parser returnArray/stream of documents
Kubernetes useOne resource per document

Best Practices

โœ… Do This:

# Space after hash
port: 8080  # http port                                      # โœ…

# Quote values containing hash
url: "https://example.com/#section"                          # โœ…

# Start each bundled document with ---
---                                                          # โœ…
kind: Service

# Keep each document self-contained
---                                                          # โœ…
kind: Deployment

โŒ Don’t Do This:

# Unquoted hash in value
url: https://example.com/#section                            # โŒ (truncated)

# Hash inside block scalar treated as comment
script: |
  # this is content                                           # โœ… (actually correct)

# Anchor across documents
---                                                          # โŒ
a: &x 1
---
b: *x                                                        # โŒ (unknown anchor)

# Marker not at line start
key: value ---                                               # โŒ (not a separator)

Common Pitfalls

PitfallWhy It HappensFix
URL truncated at #Unquoted hashQuote the value
Anchor unknown in next documentAnchors do not cross ---Define anchors per document
Parser returns only first documentParser does not expose streamUse a stream-aware API
--- inside block scalar treated as separatorBlock scalar content is not parsedIndent correctly under | or >
Comment after block scalar indicatorContent starts on next linePut comment above the block

Real-World Examples

1. Inline comment

replicas: 3  # production minimum

2. Quoted hash in URL

redirect: "https://example.com/#/dashboard"

3. Kubernetes multi-document

---
kind: ConfigMap
---
kind: Secret

4. Explicit document end

kind: Service
...

5. Comment above a block

# startup script
script: |
  echo "hello"

6. Hash inside quoted string

color: "#FF0000"

7. Multi-document with no first marker

kind: ConfigMap
---
kind: Secret

8. Document separator with comment

--- # second document
kind: Deployment

9. Block scalar with shebang

script: |
  #!/bin/bash
  echo "start"

10. Stream of three documents

---
a: 1
---
b: 2
---
c: 3

Visual

Comment Parsing

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHERE # IS A COMMENT                                   โ”‚
โ”‚                                                         โ”‚
โ”‚  port: 8080  # comment                                  โ”‚
โ”‚         โ”‚         โ”‚                                     โ”‚
โ”‚         โ”‚         โ””โ”€โ”€โ–บ comment starts here              โ”‚
โ”‚         โ””โ”€โ”€โ–บ value ends at space before #               โ”‚
โ”‚                                                         โ”‚
โ”‚  url: "https://x/#frag"                                 โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚         โ””โ”€โ”€โ–บ quoted: # is part of the value             โ”‚
โ”‚                                                         โ”‚
โ”‚  script: |                                              โ”‚
โ”‚    # not a comment                                      โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚         โ””โ”€โ”€โ–บ block scalar: # is content                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Document Markers

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  STREAM STRUCTURE                                       โ”‚
โ”‚                                                         โ”‚
โ”‚  ---                    โ—„โ”€โ”€ start document 1            โ”‚
โ”‚  kind: ConfigMap                                        โ”‚
โ”‚                                                         โ”‚
โ”‚  ---                    โ—„โ”€โ”€ end 1, start 2              โ”‚
โ”‚  kind: Secret                                           โ”‚
โ”‚                                                         โ”‚
โ”‚  ---                    โ—„โ”€โ”€ end 2, start 3              โ”‚
โ”‚  kind: Deployment                                       โ”‚
โ”‚                                                         โ”‚
โ”‚  ...                    โ—„โ”€โ”€ end document 3              โ”‚
โ”‚                                                         โ”‚
โ”‚  Parser returns: [Doc1, Doc2, Doc3]                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Anchor Isolation

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DOCUMENT BOUNDARY = ANCHOR BOUNDARY                    โ”‚
โ”‚                                                         โ”‚
โ”‚  Document 1:                                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ defaults: &d                             โ”‚           โ”‚
โ”‚  โ”‚   timeout: 30                            โ”‚           โ”‚
โ”‚  โ”‚ service: { <<: *d }   โ—„โ”€โ”€ valid here     โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                         โ”‚
โ”‚  ---                                                    โ”‚
โ”‚                                                         โ”‚
โ”‚  Document 2:                                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ other: { <<: *d }     โ—„โ”€โ”€ ERROR          โ”‚           โ”‚
โ”‚  โ”‚ *d is unknown in this document           โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Kubernetes Bundling

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ONE FILE, THREE RESOURCES                              โ”‚
โ”‚                                                         โ”‚
โ”‚  app.yaml                                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚ ---                                      โ”‚           โ”‚
โ”‚  โ”‚ kind: ConfigMap     โ—„โ”€โ”€ applied first    โ”‚           โ”‚
โ”‚  โ”‚ ---                                      โ”‚           โ”‚
โ”‚  โ”‚ kind: Secret        โ—„โ”€โ”€ applied second   โ”‚           โ”‚
โ”‚  โ”‚ ---                                      โ”‚           โ”‚
โ”‚  โ”‚ kind: Deployment    โ—„โ”€โ”€ applied third    โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                         โ”‚
โ”‚  kubectl apply -f app.yaml                              โ”‚
โ”‚  โ†’ processes each document independently                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Comment character# outside quoted scalars
Quoted hashPart of the string, not a comment
Block scalar hashContent, not comment
Document start--- at line start
Document end... at line start
First document markerOptional
Multi-document streamDocuments independent; anchors do not cross
Kubernetes patternOne resource per document

Key takeaways:

  • The # is a comment only outside quotes. A value containing a URL fragment or a color hex must be quoted, or the parser truncates it at the hash . Inside a block scalar, the # is content, not a comment .
  • --- starts a document; ... ends one. The start marker is common because bundling resources is common; the end marker is rare because end-of-file is the implicit end .
  • Documents in a stream are independent. Anchors, aliases, and merge keys defined in one document are not visible in the next. Each document is a complete unit .
  • Kubernetes uses multi-document files to bundle related resources. A Deployment, Service, and ConfigMap can live in one file separated by ---, and kubectl apply processes each independently .
  • Both markers must be at the start of a line. A --- inside a value or mid-line is not a document separator; it is part of the scalar.

Remember: Comments are for humans; document markers are for parsers. A comment disappears; a document marker changes the structure of the stream. The # rule is simple once quoting is understood: outside quotes it starts a comment, inside quotes it is a character. The --- rule is equally simple: at line start it starts a new document, anywhere else it is text. When a URL loses its fragment or a multi-document file returns only one document, check the quoting and the parser’s stream support.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!