Build a Reliable Man Page from POD with pod2man
By the end of this guide, you will have converted a small POD document into a section 1 manual page, checked it 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 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 retained for compatibility but does nothing in current pod2man because UTF-8 is already the default on this platform.--laxis also retained for compatibility and no longer validates a manual page. Usepodcheckerfor POD validity, then usepod2manfor conversion.- 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
- The source declares its input encoding when it contains non-ASCII text.
pod2manexits successfully and creates a non-empty.1file.man ./name.1ornroff -man name.1shows the expectedNAME,SYNOPSISand description sections.- The
.THline has the intended name, section, date, release and centred header. - No system manual directory was changed until the generated page was reviewed.