Build a Reliable Man Page from POD with pod2man
pod2man turns a POD file into a real section 1 manual page, ready for man to render. By the end you will have checked that page with man and made its metadata predictable. The same workflow also handles POD embedded in a Perl script.
The route
Jump straight to the step you need, or tick off Done means at the end.
Prerequisites: the perl package, a shell, and a text editor. This takes about 10 minutes for a small document. The examples were checked with Perl 5.38.2 on Ubuntu, where pod2man reports documentation generated for perl v5.38.2.
Checkpoint: know what pod2man produces
pod2man is a wrapper around Perl's Pod::Man module. It reads POD and writes formatted *roff source. That output is suitable for a terminal through man and nroff, or for printing through troff. It does not install a page and it does not run a Perl program.
On current non-EBCDIC systems, the default output encoding is UTF-8. This is a version-sensitive detail: versions before Pod::Man 5.00 defaulted to the older roff encoding. Check your local tool before relying on a particular default:
$ dpkg-query -W -f='${Package} ${Version}\n' perl
perl 5.38.2-3.2ubuntu0.6
$ pod2man --help | sed -n '1,8p'
Usage:
pod2man [--center=string] [--date=string] [--encoding=encoding]
[--errors=style] [--fixed=font] [--fixedbold=font] [--fixeditalic=font]
[--fixedbolditalic=font] [--guesswork=rule[,rule...]] [--name=name]
[--nourls] [--official] [--release=version] [--section=manext]
1. Create a small POD source file
POD can live in its own .pod file or between Perl's =pod and =cut directives. Start with a separate file so the input and output are easy to inspect. The =encoding utf8 line declares the input encoding; it is independent of the output encoding selected later.
=encoding utf8
=head1 NAME
weather-report - print a short weather report
=head1 SYNOPSIS
weather-report [--city NAME]
=head1 DESCRIPTION
Prints a report for the requested city. If no city is supplied, the
program uses its configured default.
=head1 OPTIONS
=over 4
=item B<--city NAME>
Select the city to report.
=back
=head1 EXIT STATUS
The program returns zero when it prints a report and non-zero when the
request cannot be completed.
=cut
Keep a real program's POD close to the command's behaviour. Names, options and exit statuses in the documentation are not checked against your code by pod2man.
2. Generate a section 1 page
Give the input and output as two positional arguments. A section 1 suffix is conventional for a user command. The output is plain text containing macros such as .TH and .SH, so it is safe to review before installing anything.
$ pod2man --section=1 weather-report.pod weather-report.1
$ sed -n '1,18p' weather-report.1
.\" Automatically generated by Pod::Man 5.38.2
.\"
.nr rF 0
.if \n(.g .if rF .nr rF 1
.de Sp
.if t .sp .5v
.if n .sp
..
.de Vb
.ft CW
.nf
.ne \$1
..
.TH WEATHER-REPORT 1 "2026-09-26" "perl v5.38.2" "User Contributed Perl Documentation"
.SH NAME
The exact generated comments and date can vary. By default the date comes from POD_MAN_DATE, SOURCE_DATE_EPOCH, the input file's modification time, or the current date for standard input. Set --date when reproducible headers matter:
$ pod2man --section=1 --date=2026-09-26 weather-report.pod weather-report.1
$ grep '^\.TH' weather-report.1
.TH WEATHER-REPORT 1 "2026-09-26" "perl v5.38.2" "User Contributed Perl Documentation"
3. Read the result as a user would
Use man with the generated file before copying it into a system directory. The leading ./ matters because it tells man to open this file rather than search the installed manual path.
$ man ./weather-report.1
WEATHER-REPORT(1) User Contributed Perl Documentation WEATHER-REPORT(1)
NAME
weather-report - print a short weather report
SYNOPSIS
weather-report [--city NAME]
If you only need a non-interactive check, render through nroff and inspect the first lines:
$ nroff -man weather-report.1 | sed -n '1,24p'
WEATHER-REPORT(1) User Contributed Perl Documentation WEATHER-REPORT(1)
NAME
weather-report - print a short weather report
4. Set useful headers deliberately
The page name is normally derived from the input filename and uppercased. Use --name when the filename is generic or when reading from standard input. Use --release for the project version and the centred-header option for a project header. These values are metadata in the .TH line, not content in the NAME section.
$ pod2man --name=weather-report --section=1 \
--release=2.4.0 --center='Acme Tools' \
--date=2026-09-26 weather-report.pod weather-report.1
$ grep '^\.TH' weather-report.1
.TH WEATHER-REPORT 1 "2026-09-26" "2.4.0" "Acme Tools"
--official changes the default centred header to indicate a standard Perl release, unless an explicit centred header is also given. It does not make a third-party command official, so leave it out for ordinary project documentation.
5. Choose encoding and error handling consciously
Use the default UTF-8 output for modern Linux systems. --encoding=roff is a compatibility option for old formatters and has poor results for many non-ASCII characters. It does not declare the input encoding. Keep =encoding utf8 in the POD when the source contains Unicode.
The default error style is die. A malformed POD document makes pod2man abort, normally with exit status 255. That is the safest choice in a build. --errors=stderr reports formatting errors but continues where possible; --errors=pod puts a POD ERRORS section in the generated page. Do not use those recovery modes as a substitute for fixing documentation.
$ pod2man --errors=die weather-report.pod weather-report.1
$ test -s weather-report.1 && echo 'man page generated'
man page generated
6. Handle scripts and multiple files
For a Perl script containing POD, pass the script as the input. pod2man extracts its documentation and does not execute the script. Add --name if standard input is involved, because the default name for standard input is STDIN.
$ pod2man --name=weather-report --section=1 bin/weather-report bin/weather-report.1
You can supply several input and output pairs in one invocation. Each pair is processed in order:
$ pod2man \
--section=1 bin/weather-report bin/weather-report.1 \
--section=5 conf/weather-report.conf.pod weather-report.conf.5
For a batch build, add --verbose to print each output filename. The command exits with status 1 if any document does not produce output. With default fatal POD error handling, a syntax error can stop processing earlier pairs, so check the exit status in your build script.
Common traps and safe boundaries
- Do not overwrite an installed page while experimenting. Write to a temporary or project-local filename, then review it.
- Installing system-wide needs care. Installing under
/usr/share/man/man1requires elevated privileges and changes what other users see. Prefer a package build or a user-local manual path. If you deliberately copy a reviewed page there, remove that exact file to undo the change. --utf8is a no-op. It is retained for compatibility but does nothing in current pod2man because UTF-8 is already the default on this platform.--laxdoes not validate. It is also retained for compatibility and no longer validates a manual page. Usepodcheckerfor POD validity, then usepod2manfor conversion.- Guesswork can surprise you. Default guesswork can format text that resembles Perl functions, variables or man-page references. Use
--guesswork=nonewhen documenting another language and want only explicit POD markup to control emphasis.
Done means
- Encoding declared. The source declares its input encoding when it contains non-ASCII text.
- Page generated.
pod2manexits successfully and creates a non-empty.1file. - Rendering checked.
man ./name.1ornroff -man name.1shows the expectedNAME,SYNOPSISand description sections. - Metadata is right. The
.THline has the intended name, section, date, release and centred header. - Nothing installed early. No system manual directory was changed until the generated page was reviewed.