#+TITLE: Deploying #+DESCRIPTION: Producing a production build, and putting it somewhere. #+LEDE: The output is a directory of files. Everything after that is your host's problem. * The production build #+BEGIN_SRC sh orgo build content -o _site --strict #+END_SRC Two differences from the build you run while writing: - =--strict= turns broken internal links and parse diagnostics into a non-zero exit, so a bad build fails rather than shipping. - No =--drafts=, so pages marked =#+DRAFT:= stay out. Everything in =_site= is the site: HTML, the generated =syntax.css=, =theme.css= if the config names a [[file:02-configuration.org][theme]], and every asset copied from the source. There is no runtime, no server requirement and no build step downstream. * Set base_url for production #+BEGIN_SRC toml [site] base_url = "https://example.com" #+END_SRC Relative URLs work anywhere, which is why the default is empty — but two things need absolute ones: *feeds*, because a feed is read away from the site that served it, and *canonical links*. Without a base URL the =absolute= filter is an error rather than a quietly relative link, so a feed template will tell you. No trailing slash. * One thing to exclude The build writes =.orgo-cache.json= into the output directory. It is a dot-file, so most static hosts ignore it, but it is not part of the site — exclude it if your host uploads everything: #+BEGIN_SRC sh rsync -a --delete --exclude '.orgo-cache.json' _site/ user@host:/var/www/site/ #+END_SRC Keeping the cache *between* deploys, where the CI runner can see it, is what makes CI builds incremental. Keeping it on the *server* achieves nothing. * Continuous integration #+BEGIN_SRC yaml name: build on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo install --path . - run: orgo build content -o _site --strict - uses: actions/upload-artifact@v4 with: name: site path: _site #+END_SRC =--strict= is the point of running this in CI at all: it turns a broken link into a failed build. ** Caching between runs Cache =_site/.orgo-cache.json= *and* =_site= together, or not at all. The manifest describes files it expects to find; a cache without its outputs simply triggers a full rebuild, which is correct but pointless. Given how fast a full build is — a 179-page site in well under a second — caching CI builds is rarely worth the configuration. * Static hosts Nothing here is orgo-specific; a built site is ordinary static files. - *Netlify, Vercel, Cloudflare Pages*: publish directory =_site=, build command =cargo install --path . && orgo build content -o _site --strict=. - *GitHub Pages*: upload =_site= as the Pages artifact. - *Any web server*: copy =_site= to the document root. ** URLs end in .html orgo writes =blog/post.html= and links to it that way, so the site works with no server configuration at all — including opening it from a filesystem path. If you prefer extensionless URLs, that is a server-side rewrite, and you should also set =base_url= and check that your rewrite rules do not break the relative links in the pages. * Checking a build before shipping #+BEGIN_SRC sh orgo build content -o _site --strict orgo serve content -o _site #+END_SRC Serving the production build locally is the last check worth doing: it catches a missing asset or a broken relative link in the browser, where you would notice. * What a clean build looks like #+BEGIN_EXAMPLE built 182 page(s) (182 rendered, 0 cached), copied 3 asset(s) from content -> _site (0 unresolved link(s), 0 diagnostic(s)) #+END_EXAMPLE Both zeros matter. Unresolved links are internal links pointing at nothing; diagnostics are malformed org that degraded rather than failing. With =--strict= neither can reach this line, because either would have failed the build. * Do not publish the cache =/.orgo-cache.json= is a build artefact that happens to live in the output directory, because it describes exactly that directory. Nothing breaks if it is served — it holds hashes and paths, not secrets — but it is not part of your site, so leave it behind: #+BEGIN_SRC sh rsync -r --delete-before --exclude '.orgo-cache.json' _site/ server:/var/www/example.com/ #+END_SRC Anything that uploads a directory wholesale needs the same exclusion. A deploy that *deletes* it on the far side is worse than one that copies it: the next build then has no cache to reuse and re-renders everything. * Telling a search engine where things are A build with =site.base_url= set writes =sitemap.xml= at the site root, listing every page. Point a =robots.txt= at it if you want one: #+BEGIN_EXAMPLE Sitemap: https://example.com/sitemap.xml #+END_EXAMPLE =robots.txt= is an ordinary file — put it beside your org files, or in a directory named by =[build] assets=, and it is copied through. * After an upgrade The first build on a new version is worth running with =--no-cache=, so you compare the new output to the old rather than to a cache written by both. What a version number promises — and what it does not — is in [[file:11-versioning.org][Versioning and upgrades]].