Drop .md files in your docroot and they are served as fully rendered
HTML pages - no build step, no CMS, no database. Pages are generated on
first request and cached as static HTML. Content is portable, version
control friendly, and works with any deployment workflow.
For the full motivation behind lazysite, see Motivation.
lazysite runs on its own built-in server, or on any standard web server via CGI and error-handler mechanisms.
perl tools/lazysite-server.pl runs the full processor on a non-privileged port with no configuration; ideal for development, previews, and small internal or home useFallbackResource manuallyerror_page 403 404 to point to the CGI scriptIf you use a front end other than the shipped Apache, nginx or built-in server and you intend to protect static files with ACLs, read Your web server has to co-operate in Authentication. A web server that answers an existing file from disk never reaches lazysite, so a per-file access rule cannot apply - and the failure is silent. Sites that do not use ACLs are unaffected and need nothing.
FallbackResource configurationlibtext-multimarkdown-perllibtemplate-perllibwww-perl (for remote .url sources and oEmbed)JSON::PP (Perl core - no separate install needed)Optional:
libtemplate-plugin-json-escape-perl (required for the search index)HestiaCP is supported with a dedicated installer. For other environments see the manual installation section below.
The installer registers lazysite as a HestiaCP web template. Once installed, apply it to any domain from the control panel and the processor and starter files are deployed automatically on rebuild.
git clone https://github.com/OpenDigitalCC/lazysite.git
cd lazysite
sudo bash install.sh --docroot /path/to/public_html --cgibin /path/to/cgi-bin
install.sh is a thin wrapper around install.pl; the Perl
installer reads release-manifest.json and tracks installed
state at {docroot}/lazysite/.install-state.json so re-runs
upgrade in place without losing content you've edited. See
Upgrading below.
Then in HestiaCP:
lazysiteFor Apache without HestiaCP, install the Perl dependencies and configure the vhost manually:
apt install libtext-multimarkdown-perl libtemplate-perl libwww-perl
Copy lazysite-processor.pl to your cgi-bin/ directory and make it executable:
cp lazysite-processor.pl /var/www/example.com/cgi-bin/
chmod 755 /var/www/example.com/cgi-bin/lazysite-processor.pl
Copy the starter files to your docroot:
mkdir -p /var/www/example.com/public_html/lazysite/templates/registries
mkdir -p /var/www/example.com/public_html/lazysite/layouts
mkdir -p /var/www/example.com/public_html/lazysite-assets
cp starter/lazysite.conf.example /var/www/example.com/public_html/lazysite/lazysite.conf
cp starter/lazysite/templates/registries/*.tt /var/www/example.com/public_html/lazysite/templates/registries/
mkdir -p /var/www/example.com/public_html/lazysite/templates/system
cp starter/lazysite/templates/system/*.md /var/www/example.com/public_html/lazysite/templates/system/
cp starter/index.md /var/www/example.com/public_html/
Add to your Apache vhost configuration:
DirectoryIndex index.html index.htm
FallbackResource /cgi-bin/lazysite-processor.pl
<Location /lazysite>
Require all denied
</Location>
<Directory /var/www/example.com/public_html>
Options -Indexes +ExecCGI
AllowOverride All
</Directory>
Ensure the web server user can write to the docroot:
chown ispadmin:www-data /var/www/example.com/public_html
chmod g+ws /var/www/example.com/public_html
The setgid bit (s) ensures new subdirectories created by the processor
inherit the www-data group automatically.
Clone the repository and run the built-in development server:
git clone https://github.com/OpenDigitalCC/lazysite.git
cd lazysite
perl tools/lazysite-server.pl
Open http://localhost:8080/ to browse the starter site. No Apache configuration required for local development.
layout.ttlayout: and theme: in lazysite.confpublic_html/lazysite/nav.conf to define your site navigationpublic_html/index.md for your home page content.md files anywhere in the docrootPages are available immediately at their extensionless URL:
public_html/about.md -> https://example.com/about
public_html/services/hosting.md -> https://example.com/services/hosting
public_html/services/index.md -> https://example.com/services/
Directory index pages are served when a trailing slash URL is requested.
Create dirname/index.md for any directory that needs an index page.
Four audience-specific briefings live under starter/docs/:
ai-briefing-authoring.md - writing contentai-briefing-layouts.md - designing layouts and themesai-briefing-configuration.md - configuring a siteai-briefing-development.md - working on the codebaseFeed the relevant briefing to an AI assistant at the start of a session to enable help without needing to explain the system each time. In Claude Projects, save it as a project document. For other AI tools, paste it as context at the start of the conversation.
Re-run install.sh against the same --docroot and --cgibin to
upgrade. Seed files you have edited (starter pages, docs) are
preserved; code files (processor, plugins, manager UI) are
always refreshed.
sudo bash install.sh --docroot /path/to/public_html --cgibin /path/to/cgi-bin
Before applying an upgrade, the installer creates a backup
tarball at {docroot}/lazysite/backups/. Inspect the plan
before committing:
bash install.sh --docroot /path/to/public_html --cgibin /path/to/cgi-bin --dry-run
backup_retention in lazysite.conf controls how many
backups are kept (default 3; 0 = keep all).
bash install.sh --docroot /path/to/public_html --restore
Restores the most recent backup. For a specific backup:
bash install.sh --docroot /path/to/public_html --restore --backup /path/to/backup.tar.gz
List available backups:
bash install.sh --docroot /path/to/public_html --list-backups
Restore does not touch runtime state (auth users, cache, logs) and invalidates the rendered HTML cache afterwards.
There is no uninstall script. Removal is manual, and deliberately so - the
installer only ever adds files to a domain's cgi-bin/ and the Hestia template
directory, so there is nothing to unwind beyond deleting them.
To remove lazysite from one domain, delete the engine scripts the installer
placed in that domain's cgi-bin/ (lazysite-processor.pl, lazysite-auth.pl
and the other lazysite-*.pl entry points) and restore the web-server
configuration that was in place before.
A domain's content is not touched by any of this. The public_html tree -
pages, lazysite/, uploads - is yours and stays exactly where it is. Back it up
before removing anything if you intend to move it elsewhere.
public_html/
lazysite/
lazysite.conf <- site configuration
nav.conf <- navigation (YAML)
layouts/
LAYOUT/
layout.tt <- layout template
layout.json <- layout metadata (optional)
themes/
THEME/
theme.json <- theme manifest
main.css <- theme stylesheet
assets/ <- theme assets
manager/ <- manager chrome (internal; D013)
layout.tt
assets/manager.css
templates/
registries/
llms.txt.tt
sitemap.xml.tt
feed.rss.tt
feed.atom.tt
auth/
users <- built-in auth users
groups <- built-in auth groups
forms/
handlers.conf <- named dispatch handlers
smtp.conf <- SMTP connection settings
assets/
css/
img/
js/
cgi-bin/
lazysite-processor.pl
lazysite-auth.pl
plugins/form-handler.pl
plugins/form-smtp.pl
lazysite-manager-api.pl
404.md
index.md
MIT
lazysite was developed interactively with Claude (Anthropic). Architecture, design decisions, security review, and deployment were directed by the author. Claude assisted with code generation, documentation, and iterative refinement throughout development.