Documentation
Docs
Introduction
Getting Started
Tutorial: build an app
Core Concepts
Routing
Server-Side Rendering
Mimir, state management
Pages & Layouts
API Routes
Styling & Theming
Building & Deploying
API Reference
Guides
Recipes
FAQ (use cases)
Production deploy
Deploying a rune app is a short runbook, not a research project: build the client on your machine, ship the folder plus the runtime to a Linux host, run it under systemd, and put nginx + Let's Encrypt in front. No build farm on prod, no containers required. This is the exact shape used to put this docs site on the internet.
What ships
The prod box needs no Rust/.NET toolchain and no esbuild. Ship three things:
- The runtime , the
ekkobinary + its native lib (EkkoNative.soon Linux), built elsewhere. On a
host already running another rune app, it is already installed at /usr/local/bin.
- The app bundle , your project folder including the prebuilt
.ekko/build(made by
ekko build --client on your machine) and any dependency .ekl archives it resolves from ekko.lock.
- The ekko store ,
~/.ekko/store(the packagesekko.locksources from"store",@ekko/react,
@ekko/react-dom, and any libraries like @ekko/asgard).
1. Build the client (dev machine)
2. Ship it
3. Install the runtime + system libraries (host)
The ekko binary dynamically links the WebKitGTK/GTK3 + libxdo stack (it carries the desktop/webview
feature), so those libraries must be present even on a headless server, or it will not start:
4. Extract the app + store (host)
Running
ekko build --clienton prod fails with "esbuild not found", that is expected. The client is prebuilt and shipped; only rebuild it on your dev machine.
5. Run under systemd
/etc/systemd/system/myapp.service:
[Unit]
Description=My rune app (SSR)
After=network.target
[Service]
Type=simple
User=ubuntu
Environment=HOME=/home/ubuntu
Environment=PORT=3100
WorkingDirectory=/home/ubuntu/apps/myapp
ExecStart=/usr/local/bin/ekko run server.tsx --allow=fs,net,env
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target6. Lock the port to localhost
createApp binds 0.0.0.0; keep it private and let nginx reach it on loopback. One boot-persistent rule:
Persist it with a tiny oneshot unit (ExecStart the same iptables line, RemainAfterExit=yes,
enable it) so it survives reboots.
7. nginx reverse proxy
/etc/nginx/sites-available/myapp.example.com:
server {
listen 80;
listen [::]:80;
server_name myapp.example.com;
location / {
proxy_pass http://127.0.0.1:3100;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
}
}8. HTTPS with Let's Encrypt
certbot obtains the cert over HTTP-01 (needs port 80 reachable, which it is), rewrites nginx to add the
443 ssl block, and --redirect adds the 80→443 redirect. It also installs the auto-renewal timer.
9. Verify (from anywhere)
Updating later
The runtime rarely changes; usually only the app does:
Running several rune apps on one host
Give each app its own port, systemd unit, firewall rule, and nginx server block (one server_name per
subdomain). They share the runtime and the store. That is exactly how a single host serves multiple rune
sites side by side.
Gotchas
EkkoNative.somust sit next to theekkobinary, or the runtime will not load its native APIs.- The
ekkobinary needslibxdo3+libwebkit2gtk-4.1-0+libgtk-3-0to start (it links the desktop
stack even when unused).
- Build the client on dev and ship
.ekko/build; do not rebuild on prod. - Any dependency
.eklreferenced byekko.lockmust be present at itssourcepath on the host too.
That completes Building & Deploying. Next: the exhaustive API Reference.