Quarto website preview server hacks

Do you want to build an enormous quarto website? Strap in for a bumpy ride. Here, I have a seatbelt for you.

October 7, 2024 — October 7, 2024

academe
faster pussycat
how do science
javascript
julia
language
making things
plain text
premature optimization
python
R
writing
Figure 1: quarto preview: Serving web.
tl;dr

Don’t use quarto preview for large sites. Use caddy instead.

I spent a long time fighting with the preview server (i.e. the one that spins up when we invoke quarto preview) for quarto websites and eventually the list of information and hacks grew to deserve a page of its own.

The blog you are reading right now, which is itself a quarto website, is probably much bigger than anything that the developers of quarto experience in general and as such it exposes many deficiencies in the preview server that teensy little websites might not.

Fear not! There are hacks to make it work better. Let me share the ones I know.

This page is dismissive of the quarto preview server, so you might think I am not a massive fan of quarto.

I am a massive fan of quarto insofar as I am a fan of anything, which is to say, grudgingly. It is an incredible bit of infrastructure that makes my life better. It is with deep love, and respect borne of my excellent experience, that I say: Quarto is so good that I will keep on using it despite how much the preview server sucks.

TBH it probably would not make sense for the developers of quarto to spend time on my use case; they will help more people by fixing other things.

In a world where I had leisure to contribute to open source, I would probably try to fix the preview server, but that is not what I am being paid for and not what I wish to do in my scanty free time, so for now, here are easy, pragmatic workarounds that are pretty good and much easier that touching that giant and baffling web-app.

1 quarto preview is too fancy

First caveat: The quarto preview server invoked by quarto preview, is, per default, slightly too clever for my taste.

Excessive cleverness 1: It tries to do something fancy with process management. I am not sure what the nature of the fanciness is, but the upshot is that the server is a mediocre citizen of the command-line environment. If I run it in the background it magically daemonises or something, which makes it hard to kill. If I run it in the foreground, it is reluctant to die when I press ctrl-c. This is especially annoying because sometimes the build process will hang and cannot be quit from the CLI. One reason this seems to happen if a template pops the EJS stack, because I am building a custom listing or something. The server process is a deno executable, so the following will salvage the situation:

killall deno

However, if I am running other deno processes on my computer, this will kill those too. I do not otherwise use deno, so I leave off my problem-solving there.

OTOH, if I run the preview server at the same time as a render process, it will die spontaneously sometimes.

Excessive cleverness 2: I am discombobulated when the quarto server tries to persuade my browser to switch “latest” updated page, since I am usually editing a few pages at once, and do not enjoy having my 7 open tabs suddenly decided to show me the same thing, instead of the 7 different things I wished to see. Infuriatingly, the back button does not work to undo this. Avoid this behaviour with

quarto preview --no-navigate

Excessive cleverness 3: Quarto chooses a new random port for the server each time, which is cute, but makes those 7 preview tabs impossible to bookmark and terrible for my browser workflow. I guess it is trying to optimise the possibility that if I run lots of servers, they will work? But I don’t have the many, many gigabytes of ram and CPUs that would be required to run multiple copies of this app, so that is no use to me. I fix a predictable port thusly:

quarto preview --port 8887

Putting these together, my invocation for a preview is

quarto preview --port 8887 --no-navigate --no-browser

We shold be equivalently able encode that in a project setting via _quarto.yml:

project:
  type: website
  output-dir: _site
  preview:
    port: 8887
    browser: false
    navigate: false

However that does not work for me; I find I need to set the CLI flags instead.

2 quarto preview server is broken

tl;dr Efficient, reliable, convenient: choose none.

  1. quarto preview uses colossal amounts of RAM for my site; I guess it is serving the site from memory?

    Does 2.1GB seem like a lot of memory to serve small static files to a single user?

    Does 2.1GB seem like a lot of memory to serve small static files to a single user?
  2. Despite that, the quarto server itself is not that fast at serving files. It seems to block a lot and take ages to serve me a page, even if it has already rendered the page. A profile in the browser shows my HTML is being served at approximately 400 bytes/second, which is faster than typing, but not by much.

    36.7s to serve 15KB

    36.7s to serve 15KB
  3. quarto preview also burns a surprising about of compute. It tanks my battery if I try to edit my blog on the road, much more rapidly than, for example, running a full-featured realtime audio workstation with live effects processing.

  4. quarto preview sometimes serves stale versions of the content I am working on. I might see an old version of some page, even if I have seen it tantalisingly serve the updated version momentarily, before it reverts to some older version. This annoyance is worse for me than it might sound, because I waste attention each time it happens trying to work out if the problem is quarto or me. For something I do hundreds of times per day like edit my blog, it adds up to lots of time spent swearing and debugging, rather than writing.

    If the preview server is not spending all that RAM on keeping the content current, and all that conpute time on wondering whether the content is current, what is it actually doing?

    The precise degree of slowness is particularly corrosive to my personal attention span. 30 seconds is long enough to drag productivity to a halt, but not long enough to let me go away and do something else.

  5. A full re-render via quarto render while also running the preview server behaves unpredictably. Sometimes it crashes the preview server, or leaving detritus lying around. So the server is not set-and-forget, but rather a thing I need to babysit, and make sure it does not clash with a full re-render.

  6. Sometimes, and I do not know why, the server decides a full-re-render of the site is necessitated, although I have done nothing special, and then previewing that next one-line change needs a 12 17 minute wait.

2.1 Workaround 1: continually restart

My fix for (some of) the weird bugs and misfeatures in quarto preview is to continually turn the preview server off and on again, which seems to keep memory use under control and keeps the pages more current. This is best done manually, by running it from the shell and doing a ^C to kill it.

I attempted to define a helper function which kills the quarto process and restart it automatically, but the wily quarto process seems to evade my attempts to kill it by spawning detached subprocesses or something, so it doesn’t work. This is how far I got.

quarto_preview_restart --restart-time 300 --port 9888 --no-navigate --no-browser --no-watch-input

However, that does not work. quarto preview seems to detach if I run it in the background, and I do not know how to find the correct PID to kill it. Suggestions welcome.

Seeing this, I became enlightened. The best way to stop the quarto preview server is to never start it in the first place. Read on.

2.2 Workaround 2: use a different web server

You know what? I don’t even know why I spent time trying to get quarto’s preview server to work. HTTP servers are solved. I use caddy because I am familiar with it and it is small and reliable. As with probably every web server on that list, it is much faster and more reliable than quarto preview.

caddy file-server --listen 127.0.0.1:9889 --root _site

While that guy is running in the background, I manually render the files I need from the CLI.

quarto render notebook/gd_adaptive.qmd

The VS Code quarto extension has a keyboard shortcut to do that too, which works ok, but using that I need to confirm that I wish to render HTML every time, so the CLI is quicker.

I then manually reload my browser to see the changes, but that is a small price to pay for actually seeing the changes, not some arbitrarily stale content, and moreover, seeing them in under 17 minutes, which is how long it took the quarto preview server to serve me a page after a full re-render.

There is a file-watching, fancy-pants server for VS code, Live Server. I do not use that because it does not like serving files that I have hidden from the file explorer list, and I like hiding the HTML site output from the file explorer list to keep things tidy.