fyra

Push any directory. Get a live URL.

Getting started

Installing Fyra

The CLI is a single self-contained binary. Install it globally once and you're done. Works on macOS, Linux, and Windows.

Install the CLI
terminal
$ curl -fsSL https://fyra.sh/install.sh | sh

Installs fyra to /usr/local/bin.

Create an account
terminal
$ fyra register Email: you@example.com Password: ········ Confirm password: ········ Account created. Check your email to verify your address.
Log in
terminal
$ fyra login Email: you@example.com Password: ········ Logged in successfully.

Your token is saved to ~/.fyra/config.yaml.

Already have an account? Skip registration and run fyra login directly.

Getting started

Your first deploy

Two commands: fyra create registers the app, fyra push sends it to the network.

Go to your project directory
terminal
$ cd my-site/dist
Create the app
terminal
$ fyra create --appname my-site Created app: my-site Run 'fyra push' to deploy this directory.

Writes .deploy.yaml to your project. Omit --appname to get a random slug like quiet-river.

Push it live
terminal
$ fyra push Scanning my-site... Packing 24 files (840.0 KB) Uploading... 840.0 KB sent Deploying on server... Done: https://my-site.apps.fyra.sh

Files are tarballed in memory, sent over gRPC, and the CDN updates automatically.

Subsequent deploys? Just fyra push again. That's the whole loop.

Domains

Custom domains

Three steps: register the domain with Fyra, add a CNAME at your registrar, then verify.

Register the domain
terminal
$ fyra set-domain shop.example.com Custom domain set to shop.example.com. Point your CNAME for shop.example.com → shop-example-com.ignite.fyra.sh Run 'fyra check-domains' to verify once DNS propagates.
Add the CNAME record

At your registrar / DNS provider, add:

dns record
Type Host Value CNAME shop.example.com shop-example-com.ignite.fyra.sh

Apex domains are also supported. Cloudflare users should enable Proxied mode.

Verify and activate
terminal
$ fyra check-domains Have you added a CNAME for shop.example.com → shop-example-com.ignite.fyra.sh? [y/N] y ✓ CNAME verified. Certificate status: provisioning… ✓ Custom domain is live at https://shop.example.com

To revert to slug.apps.fyra.sh, run fyra remove-domain.

Domains

SSL & HTTPS

Every Fyra site is HTTPS-only. Certificates are issued and renewed automatically.

Free subdomain certificates

*.apps.fyra.sh uses a wildcard certificate managed by Fyra. Nothing for you to do.

Custom domain certificates

Let's Encrypt issues a certificate automatically when the CNAME is detected. fyra check-domains waits for the cert to land (about 2 minutes).

certificate lifecycle
CNAME verified → cert requested → cert issued → HTTPS live
Automatic renewal

Certificates auto-renew before expiry. HTTP traffic is redirected to HTTPS.

Cert not issued? Usually a bad CNAME. Run fyra check-domains and read the diagnostic.

Customization

Custom 404 page

Drop a 404.html in your project root and Fyra serves it for not-found URLs instead of the default plain-text response.

Create a 404.html file
project structure
my-site/ ├── index.html ├── about.html ├── 404.html ← your custom error page └── styles/ └── main.css
Write your page
404.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Page not found</title> </head> <body> <h1>404</h1> <p>Nothing here. Try the <a href="/">home page</a>.</p> </body> </html>
Deploy it
terminal
$ fyra push Scanning my-site... Packing 25 files (842.3 KB) Custom 404 page detected. It will be served for not-found URLs. Uploading... 842.3 KB sent Done: https://my-site.apps.fyra.sh

No 404.html? Visitors see a plain 404 Not Found text. Optional but recommended.

Customization

Password protection

Add a .auth file to password-protect your site or any part of it. Visitors get a standard browser login prompt; valid credentials are remembered for the session.

Paid feature. Password protection requires a paid plan. Free-tier pushes containing a .auth file are rejected until you upgrade.

Create a .auth file

One username:password per line. Lines starting with # are comments. Passwords may contain :; only the first colon splits.

.auth
# one user:password per line alice:s3cret-pa55word bob:hunter2
Place it where you want the lock

The file's location sets its scope. A root .auth protects the whole site; one inside a folder protects that folder and everything under it. The deepest .auth on a path wins.

project structure
my-site/ ├── index.html ← public ├── .auth ← protects the whole site └── admin/ ├── .auth ← protects /admin/* with its own logins └── page.html
Push and verify
terminal
$ fyra push Done: https://my-site.apps.fyra.sh $ curl -I https://my-site.apps.fyra.sh/admin/page.html HTTP/2 401 www-authenticate: Basic realm="my-site.apps.fyra.sh"

Credentials never ship to visitors. Passwords are bcrypt-hashed during deploy, the file itself is blocked from download along with every other dotfile, and protection runs at the CDN edge, so unauthorized requests never touch your origin. An empty or comment-only .auth fails the push on purpose, so a typo can't silently unprotect anything.

Customization

Redirects

Add a .redirects file to send old URLs to new homes. Redirects are served straight from the CDN edge in microseconds with no origin round trip, and they survive origin outages.

Premium feature. Redirects are available on premium plans. Free-tier pushes containing a .redirects file are rejected until you upgrade.

Create a .redirects file

One redirect per line: source target [status]. Status is one of 301, 302, 307, 308 and defaults to 301. Lines starting with # are comments.

.redirects
# source target [status] /old-page /new-page /moved /elsewhere 302 /docs/legacy https://docs.example.com/modern
Push and verify
terminal
$ fyra push Done: https://my-site.apps.fyra.sh $ curl -I https://my-site.apps.fyra.sh/old-page HTTP/2 301 location: /new-page x-cache: RULE

x-cache: RULE means the edge answered directly; your origin was never contacted.

Remove them later

Delete the lines (or the whole file) and push again; the redirect set is fully replaced on every deploy, so nothing lingers.

Good to know. Sources match one exact path (/old does not catch /old/x) and query strings are preserved automatically. A redirect wins over a real file at the same path. The filename must be exactly .redirects: a near-miss like .redirect fails the push with a rename hint instead of silently doing nothing.

Customization

Pretty URLs & SPA hosting

Two switches control how URLs map to files. Both live in the config block of your project's .deploy.yaml, the file fyra create wrote when you made the app.

Edit .deploy.yaml
.deploy.yaml
config: clean_urls: true spa_redirect: /index.html

Both keys are optional; set the ones you want and push.

clean_urls: drop the .html

With clean_urls: true, extensionless URLs resolve to their file, and directories resolve to their index page. Link to /about, not /about.html.

url → file
/about → about.html /blog → blog.html or blog/index.html /docs/ → docs/index.html
spa_redirect: one shell for every route

For single-page apps (React, Vue, Svelte, and friends), set spa_redirect to your shell page. Any URL that matches no file serves the shell instead of a 404, and your client-side router takes over.

.deploy.yaml
config: spa_redirect: /index.html

The value must be a site-absolute path such as /index.html or /app.html.

Push
terminal
$ fyra push Done: https://my-site.apps.fyra.sh

Config changes apply on the next push, per app.

They combine. With both set, an extensionless URL first tries the file (/about → about.html), then the directory form, and only falls back to your SPA shell when nothing matches. That is the usual setup for an SPA with a few real static pages.

Customization

Excluding files from upload

Create a .fyraignore file using .gitignore syntax.

Create a .fyraignore file
.fyraignore
# Dependencies and build output node_modules/ .cache/ # Private config .env .env.local # Source files not needed at runtime src/ *.sketch *.psd
Verify what will be uploaded

fyra push prints the file count and total size on every push; there's no separate dry-run command.

terminal
$ fyra push Scanning my-site... Packing 18 files (210.4 KB) Uploading... 210.4 KB sent Done: https://my-site.apps.fyra.sh
What's always excluded
always excluded
.git/ version control history node_modules/ npm / yarn packages .deploy.yaml fyra app metadata

.fyraignore is often a subset of .gitignore; start by copying and trimming.