Publish a static page:
echo '# Hello, world!' | ssh kedge.dev
Your SSH key identifies your account. Markdown becomes HTML.
a function
Add a #! shebang to publish a script that runs on each request:
printf '#!/bin/bash \n uptime' | ssh kedge.dev
the kedge shortcut
Install the optional kedge shortcut, a small wrapper around
ssh kedge.dev:
ssh kedge.dev setup | sh
It also adds kedge up, a Git commit-and-push helper for source directories.
a routed site
Deploy a directory, and the files become a route tree:
~/quickstart-site$ show index.html about/index.html
── index.html
<h1>Field notes</h1>
<p>A tiny site with more than one route.</p>
<a href="/about/">How this site works →</a>
── about/index.html
<h1>About this site</h1>
<p>The directory tree becomes the URL tree.</p>
<a href="/">← Back home</a>
~/quickstart-site$ kedge upindex.html serves /; about/index.html serves /about/. kedge up
derives a default app name from the working directory.
Switch branches for an isolated preview:
~/quickstart-site$ show about/index.html
── about/index.html
<h1>Previewing a new route</h1>
<p>This only exists on the <code>tweak</code> branch.</p>
<a href="/">← Back home</a>
~/quickstart-site$ kedge upThe preview gets its own URL; main stays live.
an HTML app
Add data-kedge to ordinary HTML for shared, live data without writing an
application server, SQL, or JavaScript.
~/quickstart-clicks$ show index.html
── index.html
<!doctype html>
<title>Clicks</title>
<h1>Clicks</h1>
<strong data-kedge="counters/home">{{clicks | plural:click}}</strong>
<button data-kedge="counters/home?$increment=clicks">add one</button>
~/quickstart-clicks$ kedge upAt deploy time, Kedge parses the bindings, creates the counter column, and manages the underlying database. Increments merge without losing simultaneous clicks, and bound values update live. See HTML apps for a more detailed example.
a database and a filesystem
Every app has /shared.db and /shared/, ordinary local paths for state that
needs code. Two handlers, the same rollup:
~/quickstart-state$ show index.sh files.sh
── index.sh
#!/usr/bin/env bash
agent=${HTTP_USER_AGENT//"'"/"''"}
printf 'Content-Type: text/plain\n\n'
sqlite3 -header /shared.db "
CREATE TABLE IF NOT EXISTS hits(
id INTEGER PRIMARY KEY, dc TEXT, agent TEXT, at TEXT DEFAULT (datetime('now')));
INSERT INTO hits(dc, agent) VALUES('$KEDGE_DC', '$agent');
SELECT dc, count(*) AS hits, max(at) AS latest, agent
FROM hits GROUP BY dc, agent ORDER BY hits DESC;"
── files.sh
#!/usr/bin/env bash
echo "$KEDGE_DC $HTTP_USER_AGENT" >> /shared/agents.log
printf 'Content-Type: text/plain\n\n'
sort /shared/agents.log | uniq -c | sort -rn
~/quickstart-state$ kedge up/ groups the request log in SQL; /files appends a line and counts it with
sort | uniq -c. Both replicate across instances and regions. Use the database
when you want a query or an index. See shared data.
an app built from source
Kedge detects common frameworks and builds them from source. This Rust service reports its optimized binary and resident-memory sizes:
~/quickstart-rust$ show src/main.rs Cargo.toml
── src/main.rs
use memory_stats::memory_stats;
use std::env::{current_exe, var};
use tiny_http::{Response, Server};
fn main() {
let port = var("PORT").unwrap_or_else(|_| "8080".into());
let srv = Server::http(format!("0.0.0.0:{port}")).unwrap();
let bin = current_exe().unwrap().metadata().unwrap().len() / 1024;
for (n, req) in srv.incoming_requests().enumerate() {
let rss = memory_stats().unwrap().physical_mem / 1024;
let body = format!("release binary: {bin} KiB\n\
process RSS: {rss} KiB\nrequest: {}\n", n + 1);
req.respond(Response::from_string(body)).unwrap();
}
}
── Cargo.toml
[package]
name = "quickstart-rust"
version = "0.1.0"
edition = "2021"
[dependencies]
memory-stats = "1.2"
tiny_http = "0.12"
[profile.release]
strip = true
~/quickstart-rust$ kedge upCargo.toml is the build plan; Kedge compiles a release binary and supplies
$PORT.
a prebuilt image
Publish an existing Docker image straight from a public registry:
kedge publish "$(kedge whoami)-quickstart-httpbin" \
--image ghcr.io/mccutchen/go-httpbin:2.23.1
a Dockerfile
Use a Dockerfile when the image itself is the point. This multi-stage Go build
compiles a static binary and copies it into an empty scratch image:
~/quickstart-scratch$ show main.go Dockerfile
── main.go
package main
import "net/http"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("Hello from scratch.\n"))
})
http.ListenAndServe(":8080", nil)
}
── Dockerfile
FROM golang:1.24-alpine AS build
COPY main.go /
RUN CGO_ENABLED=0 go build -o /server /main.go
FROM scratch
COPY --from=build /server /
EXPOSE 8080
ENTRYPOINT ["/server"]
~/quickstart-scratch$ kedge upSee builds & images for the full build order.
clean up
Delete the example apps:
kedge delete 'hello-world-*' 'bash-*' \
"$(kedge whoami)-quickstart*"
beyond the defaults
Software that expects local disk, such as Postgres, a game server, or a dev box, needs a persistent volume:
kedge up --volume /data
Connecting a repository through GitHub deploys gives every pull request its own preview.