Quickstart
Run Virling on your own server, add the tracking script to your site and connect a payment provider. From then on, every new payment shows the visit that brought it.
Note
Hosted plan coming soon. app.virling.com is not open yet, so this guide covers self-hosting. Steps 4 to 6 will be the same on the hosted plan.
Warning
Payments need the next release. v0.1.0, the current release, has tracking, sign-in and onboarding. Payment sync, attribution, the dashboard,
VIRLING_BASE_URLandVIRLING_SECRET_KEYship in the next release. Until it is out, build from source frommainto use them.
You need a Linux or macOS server with Docker or one of the release binaries, and a domain pointing at it. Virling listens on port 8080 and keeps everything in one data folder: SQLite for settings, DuckDB for events.
Start the server
Pick one way to run it. Each starts the server with the app turned on, which serves sign-in, the dashboard and payment sync.
docker run -d --name virling \
-p 8080:8080 \
-v virling-data:/data \
-e VIRLING_BASE_URL=https://stats.example.com \
ghcr.io/virling/virling:latest serve --app
curl -LO https://github.com/virling/virling/releases/download/v0.1.0/virling-v0.1.0-x86_64-unknown-linux-gnu.tar.gz
tar xzf virling-v0.1.0-x86_64-unknown-linux-gnu.tar.gz
cd virling-v0.1.0-x86_64-unknown-linux-gnu
VIRLING_BASE_URL=https://stats.example.com ./virling serve --app
[Unit]
Description=Virling
After=network-online.target
Wants=network-online.target
[Service]
User=virling
Environment=VIRLING_DATA=/var/lib/virling
Environment=VIRLING_BASE_URL=https://stats.example.com
ExecStart=/usr/local/bin/virling serve --app
Restart=on-failure
[Install]
WantedBy=multi-user.target
/etc/systemd/system/virling.service, then run sudo systemctl enable --now virling.Check that it is up: curl http://localhost:8080/health prints ok.
Warning
Keep the secret key. Payment keys are encrypted with
secret.keyin the data folder, or withVIRLING_SECRET_KEYif you set it. Lose it and you have to reconnect every provider.
Add your site
Create the site and its owner from the command line. The owner account is created if it does not exist. You can also add sites after signing in.
$ virling add-site yourdomain.com \
--owner you@example.com --timezone Europe/Berlin
Added yourdomain.com. Script tag:
<script defer src="https://YOUR-VIRLING-HOST/v.js" data-site="yourdomain.com"></script>
With Docker, run it inside the container: docker exec virling virling add-site yourdomain.com --owner you@example.com. The timezone decides where your report days start and defaults to UTC.
Sign in
Open https://stats.example.com/login and enter the owner email. Without an SMTP server, the sign-in link is printed to the server log instead of being emailed. With Docker, read it with docker logs virling.
Note
Send real emails. Set
VIRLING_SMTP_URL, for examplesmtps://user:pass@smtp.example.com:465, andVIRLING_MAIL_FROMfor the sender. See Sign-in email.
Add the script
Paste this before </head> on every page. It loads with defer and never blocks rendering.
<script defer src="https://stats.example.com/v.js"
data-site="yourdomain.com"></script>
Attributes the script reads:
| Attribute | What it does |
|---|---|
data-site Required | The domain you added in step 2. Pages on its subdomains count toward it. |
data-api Optional | Send events to this URL instead of /api/event on the script host. |
data-cookieless Optional | Never set the virling_vid cookie. Visitors are counted with a daily hash, and payments show as Unattributed. |
data-respect-gpc Optional | Send nothing when the browser has Global Privacy Control turned on. |
data-dev Optional | Also track on localhost. Off by default, so local testing stays out of your reports. |
Track a goal from your code with virling("goal", "signup"). Route changes in single-page apps are tracked on their own. See Framework guides for Next.js, Astro, WordPress, Framer and Webflow.
Connect payments
In the app, open your site’s setup, then Connect payments, and paste a read-only key. Virling imports the last 90 days, converts amounts to USD and checks for new payments every minute.
Link checkout to visits
Pass the visitor ID into checkout so each payment is credited to the visit that brought it. On your server, read the virling_vid cookie. In the browser, window.virling?.vid?.() returns the same ID.
// Stripe Checkout (Node): pass the visitor ID on the session
const session = await stripe.checkout.sessions.create({
client_reference_id: req.cookies.virling_vid,
line_items: [{ price: "price_...", quantity: 1 }],
mode: "subscription",
success_url: "https://yourdomain.com/thanks",
});
Note
Payment links need nothing. Stripe Payment Links, Polar checkout links and Dodo payment links are tagged by the script when a visitor clicks them. Payments without a visitor ID still count, as Unattributed.
See Link checkout to visits for Dodo Payments, Polar and LemonSqueezy.