---
title: Quickstart
description: Deploy your first static flow in minutes
icon: Rocket
---
import { Steps, Step } from 'fumadocs-ui/components/steps';
import { File, Folder, Files } from 'fumadocs-ui/components/files';
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
This guide walks you through creating your first "Hello World" flow. Unlike traditional proxies that default to forwarding everything, Vane takes an explicit approach **doing nothing until you tell it exactly what to do**.
## What You'll Build
By the end of this quickstart, you'll have configured Vane to:
- Accept traffic on Port 80
- Identify it as HTTP protocol
- Serve a static HTML response directly from the engine, no upstream backend required
<Callout type="info" title="Hot Reload">
Vane watches for configuration changes in real-time. Simply save your files and the engine will
reload atomically. **No restarts needed!**
</Callout>
## Prerequisites
Before starting, ensure Vane is installed and running on your system. You can verify this by checking your process list or systemd status.
## Architecture Overview
We'll build the traffic pipeline from the ground up, progressing through three layers:
**Layer 4 (Transport)** → **Layer 4+ (Carrier)** → **Layer 7 (Application)**
---
## Configuration Steps
<Steps>
<Step>
### Create the L4 Listener
First, we need to open a TCP port. By default, Vane receives raw bytes. We'll tell it to upgrade this traffic to the `http` protocol resolver.
Create the file at `/etc/vane/listener/[80]/tcp.yaml`:
<Files>
<Folder name="/etc/vane" defaultOpen>
<Folder name="listener" defaultOpen>
<Folder name="[80]" defaultOpen>
<File name="tcp.yaml" />
</Folder>
</Folder>
</Folder>
</Files>
Add the following configuration to unconditionally upgrade all traffic on this port:
```yaml title="/etc/vane/listener/[80]/tcp.yaml"
connection:
internal.transport.upgrade:
input:
protocol: 'http'
```
<Callout type="tip">
The `[80]` directory name indicates the port number. Vane uses this file-based convention for
clean, declarative configuration.
</Callout>
</Step>
<Step>
### Configure the L4+ Resolver
Now that Vane identifies traffic as `http`, it needs a resolver configuration. In production, you might route based on the Host header here. For this tutorial, we'll keep it simple and unconditionally upgrade the connection to the `httpx` application layer.
Create `/etc/vane/resolvers/http.yaml`:
<Files>
<Folder name="/etc/vane" defaultOpen>
<Folder name="resolvers" defaultOpen>
<File name="http.yaml" />
</Folder>
</Folder>
</Files>
Add the upgrade configuration:
```yaml title="/etc/vane/resolvers/http.yaml"
connection:
internal.transport.upgrade:
input:
protocol: 'httpx'
```
</Step>
<Step>
### Define the L7 Application
Finally, the traffic reaches Layer 7 where we define the request handling logic. Instead of forwarding to a backend server, we'll use the `response` terminator to serve a static page directly.
Create `/etc/vane/applications/httpx.yaml`:
<Files>
<Folder name="/etc/vane" defaultOpen>
<Folder name="applications" defaultOpen>
<File name="httpx.yaml" />
</Folder>
</Folder>
</Files>
Add the response handler:
```yaml title="/etc/vane/applications/httpx.yaml"
pipeline:
internal.terminator.response:
input:
status: 200
headers:
content-type: 'text/html; charset=utf-8'
body: |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vane</title>
<style>
:root { --bg: #000; --fg: #fff; --gray: #333; --text-gray: #888; --hover: #111; }
body { background: var(--bg); color: var(--fg); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; display: flex; align-items: center; justify-content: center; height: 100vh; -webkit-font-smoothing: antialiased; }
.wrap { max-width: 800px; padding: 2rem; width: 100%; }
.brand { font-size: 0.75rem; letter-spacing: 0.2em; color: var(--text-gray); text-transform: uppercase; margin-bottom: 1.5rem; font-weight: 600; }
h1 { font-size: 3.5rem; font-weight: 800; letter-spacing: -0.05em; margin: 0 0 1rem 0; line-height: 1.1; }
.sub { color: var(--text-gray); font-size: 1.25rem; margin-bottom: 3rem; font-weight: 400; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.card { border: 1px solid var(--gray); border-radius: 8px; padding: 1.5rem; text-decoration: none; color: inherit; transition: 0.2s ease; display: block; }
.card:hover { border-color: var(--fg); background: var(--hover); }
.card strong { display: block; margin-bottom: 0.5rem; font-size: 1rem; }
.card span { color: var(--text-gray); font-size: 0.875rem; line-height: 1.4; display: block; }
@media (max-width: 600px) { h1 { font-size: 2.5rem; } .grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<div class="wrap">
<div class="brand">Flow Engine</div>
<h1>Vane is Ready to Proxy.</h1>
<p class="sub">Edit your configuration to continue exploring.</p>
<div class="grid">
<a href="https://github.com/canmi21/vane" class="card" target="_blank">
<strong>Star on GitHub →</strong>
<span>Support the project and get the latest updates.</span>
</a>
<a href="https://vane.canmi.app/docs" class="card" target="_blank">
<strong>Documentation →</strong>
<span>Read the full guides on routing and plugins.</span>
</a>
</div>
</div>
</body>
</html>
```
</Step>
</Steps>
---
## Testing Your Flow
Vane should have automatically detected your new configuration files and reloaded. Let's verify everything works!
<Tabs items={['Browser', 'cURL']}>
<Tab value="Browser">
<Steps>
<Step>
Open your web browser and navigate to [http://localhost](http://localhost) or your server's
public IP address.
</Step>
<Step>
You should see the **"Vane is Ready to Proxy"** welcome screen with a clean, dark-themed
interface.
</Step>
</Steps>
</Tab>
<Tab value="cURL">
<Steps>
<Step>
Open your terminal (bash, zsh, or fish on Linux/macOS, or PowerShell on Windows).
</Step>
<Step>
Run the following command:
```bash
curl -X GET -I http://localhost
```
</Step>
<Step>
You should receive a `200 OK` response:
```text
HTTP/1.1 200 OK
Content-Length: 2237
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Date: Tue, 20 Jan 2026 07:05:30 GMT
Keep-Alive: timeout=4
Proxy-Connection: keep-alive
```
<Callout type="success">Perfect! Your flow is working correctly.</Callout>
</Step>
</Steps>
</Tab>
</Tabs>
---
## Understanding the Flow
Because Vane is explicit by design, we defined every step of the request lifecycle. While this may seem verbose for a simple "Hello World," it demonstrates the power and flexibility of the architecture:
1. **Listener** — Accepted the TCP connection on Port 80
2. **First Upgrade** — Promoted the connection to the `http` resolver for protocol inspection
3. **Second Upgrade** — Promoted the connection to the `httpx` application for response generation
4. **Terminator** — Generated a synthetic HTML response and closed the connection gracefully
This explicit approach gives you complete control over every stage of request handling, perfect for building complex routing logic, middleware chains, and custom protocol handlers.
---
## Next Steps
Now that you have a working flow, you can explore:
- **Routing** — Add Host header-based routing in the resolver
- **Middleware** — Inject authentication, rate limiting, or logging
- **Fetch Upstream** — Forward requests to backend services
- **Advanced Protocols** — Handle WebSockets, GCI, or custom protocols