Self-host · Railway
Self-hosted analytics on Railway, with no server to manage
Self-hosted analytics on Railway is one service from the Skald image and one volume for its history. Railway gives it HTTPS; your sites send it their logs.
Self-hosted analytics on Railway means running Skald's Docker image as a Railway service with a volume at /var/lib/skald. Railway keeps it up and gives it an address; Skald keeps the history on the volume and takes your sites' logs by HTTPS. One of the ways to self-host Skald free.
30-day free trial · no card · we set it up within one working day · free to self-host
- image ghcr.io/spcaeo/skald:latestsource
- volume /var/lib/skaldhistory
- PORT 8080variable
- SKALD_SITE example.comvariable
- SKALD_INGEST truevariable
- domain https://stats.example.comHTTPS
Sample settings for one Railway service. Your own names go where example.com is.
[01] deploy
Deploy Skald on Railway in six steps
All in Railway's dashboard, plus two commands in the service's shell. There is no one-click template yet, so each step is a button you press yourself.
1. Deploy the image
New project > Deploy a Docker image:
ghcr.io/spcaeo/skald:latest. Or Deploy from GitHub repo with your fork: Railway buildsdocker/Dockerfile. Copydeploy/railway.jsonto the fork's root first, so Railway finds the Dockerfile path and the health check.2. Add a volume
Right-click the service, or use the command palette, and add a volume with the mount path
/var/lib/skald. A service with a volume runs one replica and redeploys with a short stop.3. Set the variables
SKALD_SITE,SKALD_HOSTS,SKALD_TIMEZONE,SKALD_INGEST=true,SKALD_LOG_DIRandPORT=8080. The next fold lists each one.4. Give it an address
Settings > Networking > Generate Domain, or add your own domain and the CNAME record Railway shows. Put that
https://address inSKALD_BASE_URL.5. Make the first owner
Open the service's shell with
railway sshor from the dashboard, and runskald accounts init. It prints a link to set the password.6. Find the proxy
Railway does not publish its proxies' addresses. One command in the shell prints them; set
SKALD_TRUSTED_PROXIESto their range and redeploy.
[02] variables
The Railway variables Skald reads
Set them in the service's Variables tab. Railway redeploys the service when they change.
Railway mounts a new volume owned by root. Skald's container starts as root only long enough to give /var/lib/skald to its own user, 10001, then drops to that user. So RAILWAY_RUN_UID=0 is not needed: it would run everything as root.
| Variable | Sample value and what it does |
|---|---|
SKALD_SITE | example.com: your site's host name |
SKALD_HOSTS | example.com www.example.com: the names to count |
SKALD_TIMEZONE | America/New_York: where your day starts and ends |
SKALD_INGEST | true: take the site's log lines by HTTPS |
SKALD_LOG_DIR | /var/lib/skald/ingest/example.com: where those lines are kept |
PORT | 8080: the port Railway sends traffic to |
SKALD_BASE_URL | https://stats.example.com: Skald's own address, from step 4 |
SKALD_TRUSTED_PROXIES | empty until step 6, then the range peers shows |
[03] in the shell
Two commands in the service's shell
Open the shell with railway ssh or from the service in the dashboard. Change the name and the email in the first one to your own before you copy.
$ skald accounts init --org "Example Co" --owner-email owner@example.com
Prints a link that sets the owner's password.
$ python3 /opt/skald/docker/entrypoint.py peers
Prints the addresses connected to port 8080: Railway's proxy, not you.
Why every visitor has one address until you do this
Railway's proxy opens every connection to Skald, so without step 6 each visitor has the proxy's address: one sign-in limit for everyone, and returning visitors run together.
- Leave
SKALD_TRUSTED_PROXIESempty. Open the dashboard, and while it loads, runpeers. - Set
SKALD_TRUSTED_PROXIESto the smallest private range that holds what it printed:10.0.0.0/8for 10.x.x.x,100.64.0.0/10for 100.64.x.x to 100.127.x.x. KeepSKALD_REAL_IP_HEADERat its default,X-Forwarded-For. Redeploy. - Check: Internal addresses > Add my current address must show your own public address. Remove the entry after.
If it shows another public address on every try, a CDN sits in front of the proxy. Empty the setting again and ask Railway's support which header holds the visitor's address. Skald refuses 0.0.0.0/0.
[04] your site's log
Send your site's visits to Skald on Railway
Railway has no log of your website, so the site's requests arrive by HTTPS. SKALD_INGEST=true and SKALD_LOG_DIR turn the receiving end on. Then pick where your site runs.
In the dashboard, Settings > Connections > Send this site's visits to Skald fills in the send address and a sending token for you, and ends with Test connection.
One command on the web server
On the site's nginx or Apache server, with or without Docker, one command finds the access log and sends its new lines to this Skald. Skald makes the sending token for you.
curl -fsSL https://<your Skald>/traffic-kit/<code> | sudo bash
A template: your Skald shows the real line. Its link works once, for 24 hours.
The Skald Worker, on any Cloudflare plan
A small Worker passes every request on unchanged and, after the page has gone out, posts one record of it to this Skald. Cached pages are counted too.
A Vercel log drain
Vercel posts every request to this Skald. Log drains need Vercel Pro or Enterprise; on Hobby, only the script tag is left.
The script tag alone
One line in every page's head counts people, their clicks and their forms. It never sees AI crawlers or bots: they do not run scripts.
[05] keeping it
The volume, backups and upgrades on Railway
Everything Skald keeps is on the volume: the history, accounts, settings, its own backups and the shipped log lines of the last days.
Pick the volume size
Railway lists volume sizes per plan on its own pricing page, and the trial tier's volume is small. Check it before you choose a plan.
Backups on the volume
Skald makes a nightly and a weekly copy of its history, each restored into a scratch file and checked before it counts. Run
skald backupin the shell for one now.Upgrades
Take a backup, then change the image tag in the service's settings and redeploy. The databases are brought up to date at start. Pin
:2to get every 2.x release and never a breaking one.
What it does not do on Railway
- It does not scale past one replica. The history is SQLite on one volume; never add a second copy.
- It has no log of your website on Railway, so the site's log is sent to it. A site with no server or CDN log (the script tag alone) shows no AI crawlers and no bots.
- A service without a volume loses the history at every redeploy.
- Railway does not publish its proxies' addresses, so the trusted range is yours to find and to check again after Railway changes its network.
keep reading
More on self-hosted analytics on Railway
questions
Self-hosted analytics on Railway FAQ
Straight answers, including what Skald does not do.
Is there a one-click Deploy on Railway button for Skald?
Not yet. A Railway button needs a template published from Railway's dashboard by the project's owner. Until there is one, deploy the image by hand: it takes six short steps.
Do I need RAILWAY_RUN_UID=0 for the volume?
No. Railway mounts a new volume owned by root, so Skald's container starts as root only long enough to give /var/lib/skald to its own user, 10001, then drops to that user. RAILWAY_RUN_UID=0 would run everything as root.
Why does every visitor show the same address on Railway?
Railway puts a proxy in front of the service and does not publish its addresses. Until you tell Skald which proxy to believe, every visitor has the proxy's address. Run the peers command in the service's shell, then set SKALD_TRUSTED_PROXIES to the smallest private range that holds the addresses it prints.
How big a volume does Skald need on Railway?
Railway lists volume sizes per plan on its own volumes page, and the trial tier's volume is small. Skald keeps its history, accounts, backups and the shipped logs of the last days on the volume, so check Railway's pricing page before you choose.
How does my website's traffic reach Skald on Railway?
By HTTPS posts. A site on its own server runs one command that sends its log; a site on Vercel uses a log drain; a site behind Cloudflare uses the Skald Worker. SKALD_INGEST=true and SKALD_LOG_DIR turn the receiving end on.
$ tail -f access.log | skald
See what AI does with your site. Free for 30 days.
No card. We set up hosted Skald for you within one working day, or you self-host it free. Every line of your log, sorted and added up.