Deployment
june is webhook-driven. Linear delivers a webhook → the orchestrator spawns a runner → the runner posts a comment → the issue moves state. If the host is asleep when the webhook fires, the request is dropped or retried on Linear’s schedule, and the user-facing latency is bad. Async tools want an always-on host.
This page covers where to run june and how to keep the host awake.
TL;DR
| Host | Best for | Notes |
|---|---|---|
| Linux VPS (Hetzner, DO, Linode) | Cheapest stable option. €4–8/mo. | jun install-service writes a systemd --user unit. Reverse proxy or tailscale funnel for ingress. |
| Dedicated Mac mini at home | Free if you already own one. Closest to Claude Code’s UX. | Auto-login + pmset settings below. Cloudflare Tunnel or Tailscale for ingress. |
Hosted Mac (Scaleway, MacStadium, AWS mac1.metal) | When you need macOS-specific runners but no hardware at home. | Pricier than Linux. Same setup as a local Mac. |
| Your laptop, lid open | Trying it out. Demo days. | Works while awake and on power. Lid-close = sleep = dropped webhooks unless you do clamshell. |
The orchestrator itself is tiny (no native deps, runs on Node 22.11+ with built-in SQLite). Pick the host on cost and tunnel preference, not on june’s resource footprint.
Why always-on matters
A webhook is a one-way push from Linear to your POST /webhook/linear. Linear’s delivery policy retries on connection failures, but:
- Sleeping macOS / Linux hosts don’t answer until they wake up — which can be minutes after the webhook fires (or never, if Wake-on-LAN isn’t configured).
- Failed deliveries eventually get marked stale by Linear; the issue’s first response is delayed by the retry window.
- In-flight runs (a Claude session mid-task) die when the host sleeps. The orchestrator’s reconciliation will mark them
Blockedon next boot — but you’ve lost the work.
Keep the host awake; you get the snappy “comment → 5-second-later reply” experience Linear users expect from a synchronous control plane.
Recommended: Linux VPS
The cheapest and easiest unattended host. Any cloud Linux box with Node 22.11+ works:
# On the VPS (assumes a non-root user `june` with sudo)sudo apt install -y nodejs npm # or use nvm / fnm / nodesource for 22.11+npm i -g june # see README "Private install" while the repo is privatejun setup # walk the wizardjun install-service # writes ~/.config/systemd/user/june.service, enables --nowsystemctl --user status junejun install-service on Linux writes a systemd --user unit. After install:
systemctl --user start|stop|restart june— control the daemon.journalctl --user -fu june— follow logs (orjun logs -f).loginctl enable-linger <user>— make the unit survive logout (required on most distros).
Ingress: any of caddy / nginx / traefik reverse-proxying 127.0.0.1:8787 with a Let’s Encrypt cert, or tailscale funnel 8787 if you already use Tailscale. Cloudflare Tunnel works too but is overkill on a public host.
Mac mini (or any always-on Mac)
If you already own a Mac you can leave plugged in, this is the lowest-cost option. The mini doesn’t have a battery to worry about; just configure power + auto-login and forget it.
1. Power settings — never sleep on AC
System Settings → Battery → Options (or older macOS: System Preferences → Energy Saver). Set:
- Prevent automatic sleeping when the display is off: ON.
- Wake for network access: ON (lets WoL / mDNS find the box).
- Start up automatically after a power failure: ON (Mac mini only — laptops don’t expose this).
Or do it from the terminal once and never touch the GUI again:
sudo pmset -c sleep 0 # never sleep on AC powersudo pmset -c disksleep 0 # don't spin down diskssudo pmset -c displaysleep 10 # screen off after 10 min (saves the panel; CPU stays awake)sudo pmset -c womp 1 # wake-on-networksudo pmset -c autorestart 1 # auto-restart after a power blip (Mac mini)sudo pmset -c standby 0 # disable deep sleepsudo pmset -c hibernatemode 0 # no hibernationVerify:
pmset -g # current settingspmset -g assertions # what's preventing sleep right now2. Auto-login (so launchd agents come back after a restart)
jun install-service writes a launchd user agent (lives in ~/Library/LaunchAgents/). User agents only run for a logged-in user — so after a power blip, the Mac needs to log back in by itself.
System Settings → Users & Groups → Automatic login → pick the june user. (On Apple Silicon you may need to first disable FileVault to enable auto-login; trade-off is yours.)
3. Caffeinate as a safety net
If you don’t want to touch pmset and your only goal is “keep this Mac awake for the next 8 hours of demo,” caffeinate is the one-liner:
caffeinate -dimsu & # disables display/idle/system sleep; survives until you `kill` itcaffeinate -dimsu -t 28800 # for 8 hours, then stopsThis is the “I just want this to work for now” path. Use pmset for the permanent setup.
4. Laptops: clamshell mode
If the host is a MacBook Pro you want closed:
- Plug it in.
- Connect an external display, keyboard, and mouse.
- Close the lid. macOS keeps running and uses the external display.
Without an external display, macOS sleeps when you close the lid even on AC. There’s no system-supported “lid-closed without external display” mode — third-party tools (Amphetamine, InsomniaX) override the lid-close signal, but they’re brittle across macOS updates. Just don’t close the lid, or use clamshell mode, or use a mini.
5. Ingress from a home network
You need a public URL to give Linear’s webhook. Options, in order of how much I’d recommend them:
- Cloudflare Tunnel (recommended). Install
cloudflared, run as a launchd service yourself, point Linear at the tunnel URL. Stable as long ascloudflaredis running. Free. - Tailscale Funnel. If you already use Tailscale:
tailscale funnel 8787→https://<mac>.<tailnet>.ts.net. Free, stable URL. - Port-forward on your router + dynamic DNS. Works, but exposes your home IP, and you have to manage TLS yourself.
jun setup’s tunnel step covers the first two.
Hosted Mac
When you need macOS-specific runners (e.g. an Xcode-touching Claude session) but don’t have hardware at home:
- Scaleway Apple silicon — bare-metal Mac mini M1/M2, hourly billing. Cheapest of the three.
- MacStadium — enterprise-priced; monthly contracts.
- AWS EC2 mac1.metal / mac2.metal — minimum 24-hour billing units; you pay for whole days even if you used 30 min.
Treat these like a remote Mac mini — same pmset + auto-login setup, except pmset settings often persist across the host’s life because the provider images them that way. SSH in, run jun setup, run jun install-service. Use Tailscale or Cloudflare Tunnel for ingress; the provider’s public IP is usually fine too.
Your laptop
This is the “I’m just trying it” tier. Things to know:
- june runs fine on a MacBook while you’re using it. Lid open, plugged in, screen awake = the orchestrator answers webhooks.
- The moment the laptop sleeps (lid close, idle timeout, screen off) the webhook server stops answering. Linear’s retries may eventually land — or may not.
- If you run unattended jobs on a laptop, run
caffeinate -dimsu &before walking away, and don’t close the lid. - Use
jun install-serviceif you want the orchestrator to come back automatically after a restart. Skip it if you want to babysit.
If you find yourself using caffeinate & every day, you’re past the laptop tier — get a Mac mini or rent a Linux VPS.
Verify it stays up
After whichever setup you picked:
jun status # is it running? listening where? last webhook seen when?jun doctor # full re-probe; safe to wire into cron / launchd as a heartbeatA useful smoke check on a remote host: from your laptop, hit the public webhook URL’s /health:
curl -fsS https://<your-public-url>/health# {"status":"ok"}If /health is reachable from the public internet, Linear’s webhook will reach the orchestrator too. The rest is jun doctor confirming the in-process state machine.