tools/lazysite-server.pl runs the full processor on a non-privileged port.
No Apache configuration required.
perl tools/lazysite-server.pl
Options:
--port PORT Port to listen on (default: 8080)
--docroot PATH Document root (default: ../starter)
--processor PATH Processor path (default: ../lazysite-processor.pl)
--extensions DIR Also look for extensions in DIR (repeatable; see below)
--cache Respect cache files (default: always regenerate)
--auto-index Generate an index + breadcrumb nav for a tree with no
index.md (writes nothing to the docroot)
--no-seed Never seed scaffolding into the docroot
--help Show help
With no arguments, the server serves the starter/ directory on port 8080.
To serve your own site:
perl tools/lazysite-server.pl --docroot /path/to/public_html
The server always regenerates pages by default (equivalent to
LAZYSITE_NOCACHE=1) so edits are visible immediately without deleting
cache files.
Run from the tree, the server finds extensions in the plugins/ directory beside
the docroot. An extension kept elsewhere (an app's own, in its own repository)
is named with --extensions, once for each directory:
perl tools/lazysite-server.pl --docroot /path/to/public_html \
--extensions /work/crm/extensions --extensions /work/shop/extensions
plugins/.
A name found in two of them is served from the first, and the startup lines say
so (shared.pl is in A and B: A wins).plugins/, and
named at startup.lib/ on PERL5LIB for the scripts it runs, so an
extension outside the tree finds the engine's modules.The dev server doubles as a zero-install Markdown browser: point it at any tree
and add --auto-index.
perl tools/lazysite-server.pl --docroot /path/to/tree --auto-index
In this mode it:
title, the directory's README linked as an
overview) for any directory that has no index.md;index / area / page) at the top of every rendered
page, so notes link back up without a nav.conf;LAZYSITE_CACHE_DIR) instead of {docroot}/lazysite/cache.Scaffolding (auth/forms/conf seed files) is otherwise only ever written into a
real lazysite docroot - one that already has a lazysite/ directory or a
lazysite.conf.example. Use --no-seed to force it off even there.
Set LAZYSITE_NOCACHE=1 to bypass the cache read path when running the
processor manually:
LAZYSITE_NOCACHE=1 REDIRECT_URL=/about \
DOCUMENT_ROOT=/path/to/public_html \
perl cgi-bin/lazysite-processor.pl
This forces the page to regenerate on every run. Note that cache files are
still written - LAZYSITE_NOCACHE skips reading the cache but not writing it.
The most direct way to diagnose a page error:
REDIRECT_URL=/about \
DOCUMENT_ROOT=/home/username/web/example.com/public_html \
perl /home/username/web/example.com/cgi-bin/lazysite-processor.pl
Prints full HTML output or Perl errors to the terminal. Adjust
REDIRECT_URL to the failing page path.
Override the default lazysite.conf path for testing:
LAZYSITE_CONF=/path/to/alt.conf \
REDIRECT_URL=/page \
DOCUMENT_ROOT=/path/to/public_html \
perl cgi-bin/lazysite-processor.pl
Or via command-line argument:
perl cgi-bin/lazysite-processor.pl --conf /path/to/alt.conf
Pre-render all pages for static hosting:
# Build in-place
bash tools/build-static.sh https://example.com
# Build to a separate output directory
bash tools/build-static.sh https://example.com ./dist
Deploy the output to GitHub Pages, Netlify, Cloudflare Pages, or any plain web server.
plugins/audit.pl scans the docroot and reports orphaned pages
(source files with no inbound links) and broken links:
perl plugins/audit.pl /home/username/web/example.com/public_html
Pass --exclude to omit specific pages from the orphan report:
perl plugins/audit.pl --exclude changelog,contributing /path/to/docroot
Local .md pages regenerate automatically when the .md file is newer
than the cached .html. Editing and saving is sufficient - no manual step
needed.
Remote .url pages use TTL-based invalidation (default 1 hour). The stale
cache is served immediately; the refetch happens after TTL expiry.
To force regeneration:
rm public_html/about.html
find public_html -name "*.html" -delete
Whether the homepage cache (index.html) is served by the web server or by
the processor depends on the vhost template:
The plain Hestia lazysite template (lazysite.tpl / lazysite.stpl)
has DirectoryIndex index.html index.htm. Once index.html exists, /
is served from it directly and the processor is not asked, so an edit to
index.md does not show until the cache is deleted:
rm public_html/index.html
The nginx and Apache templates and the Hestia lazysite-cgi,
lazysite-fcgi and lazysite-app templates leave index.html out of
the index list (index.htm index.shtml), and the one-rule templates
have none. / with an index.md reaches the processor, which serves
the cache and re-renders it after an edit. An index.html with no
index.md beside it is served as a static home.
Content type sidecar files (.ct) in lazysite/cache/ct/ are
managed automatically - created when a page is cached, deleted when
the corresponding .html is deleted.
To clear all cached state manually:
find public_html/lazysite/cache -delete
mkdir -p public_html/lazysite/cache
This removes both the layout cache and content type cache.
tail -50 /home/username/web/example.com/logs/example.com.error.log
End of script output before headerslazysite: Cannot write cache file ... Fix with: chmod g+ws.html to the docroot. Pages
render correctly but are not cached. Fix with:
chown ispadmin:www-data /home/username/web/example.com/public_html
chmod g+ws /home/username/web/example.com/public_html
On the plain Hestia lazysite template, Apache serves index.html
directly via DirectoryIndex, bypassing the processor. After editing
index.md, delete the cache:
rm public_html/index.html
The other shipped templates hand / to the processor, so this does not
arise on them.
Usually caused by a layout template error. The processor falls back to
minimal HTML rather than crashing, but check the error log for layout
warnings. Delete the empty .html file to force regeneration after
fixing the template.
Registries only generate when a page is rendered - not on cached serves. If missing, delete the registry file and force a page render:
rm public_html/llms.txt
rm public_html/index.html
curl -s https://example.com/ > /dev/null
perl -c cgi-bin/lazysite-processor.pl
Log level and format are set in lazysite.conf:
log_level: INFO # ERROR, WARN, INFO, DEBUG
log_format: text # text or json
Override at startup with environment variables:
LAZYSITE_LOG_LEVEL=DEBUG perl tools/lazysite-server.pl ...
LAZYSITE_LOG_FORMAT=json perl tools/lazysite-server.pl ...
Each script declares a $LOG_COMPONENT identifier and calls
log_event($level, $context, $message, %extra). Logs go to
lazysite/logs/COMPONENT.log when that directory is writable,
otherwise stderr.
Standard command for syncing local development to a deployed site while preserving runtime state:
rsync -av --delete \
--exclude='.git' \
--exclude='test-site' \
--exclude='starter/lazysite/auth/' \
--exclude='starter/lazysite/forms/contact.conf' \
--exclude='starter/lazysite/forms/handlers.conf' \
--exclude='starter/lazysite/forms/smtp.conf' \
--exclude='starter/lazysite/lazysite.conf' \
--exclude='starter/lazysite/nav.conf' \
--exclude='starter/lazysite/cache/' \
--exclude='starter/lazysite/logs/' \
/home/user/lazysite/ /srv/projects/lazysite/
Runtime state files (user credentials, form config, site config, nav, cache, logs) are excluded so local site data survives the sync.
Pages with ttl: set in front matter use
Cache-Control: public, max-age=N. When combined with Vary: Cookie
(sent on all responses), proxy caches will create separate entries per
cookie value. For public pages with ttl:, this is generally fine
since anonymous visitors share the same cache entry. For pages visible
to both authenticated and anonymous users, consider whether TTL caching
is appropriate.