Hire Laravel developers with AI expertise at $20/hr. Get started in 48 hours.

Setup Umami self-hosted analytics using Laravel Forge

Published on by

Setup Umami self-hosted analytics using Laravel Forge image

Many of us are loooking to move away from Google Analytics where possible. Reasons for doing so include:

  • Data privacy concerns
  • Avoiding cookie warnings
  • Seeking a simpler UI to share with clients/team

While looking into alternatives you'll find a range of paid, privacy-focussed SaaS analytics products, as well as some self hosted open source options (some of which are shared by companies who also offer a hosted paid service).

I wanted a self hosted option and before settling on Umami.js I also checked:

For all I know, these might be great - but I didn't get to a feature comparison or to try any of them out. I went with Umami.js simply because of how I wanted to install it on this occasion: on an Ubuntu VM, with Postgress/MySQL, and without using docker.

One nice feature of Umami.js is easily sharing a public dashboard for a website, you can take a look at their own public dashboard (pictured below) to see if it's the kind of thing you're looking for.

Instructions

These instructions are for Laravel Forge, which makes things even easier if you're using it but the installation, build instructions, and key Nginx config snippet apply to any situation.

SSH into your VM and create the database you plan to use.

mysql -u forge -p
create database umami;

Then from /home/forge/ run.

git clone https://github.com/mikecao/umami.git
cd umami
npm install
mysql -u forge -p umami < sql/schema.mysql.sql

Now create a .env file in the same folder.

DATABASE_URL=mysql://forge:PASSWORD@localhost:3306/umami
HASH_SALT=RANDOMSTRING

Replacing PASSWORD with your Forge MySql database password and RANDOMSTRING with... a random string! From your Linux server you can generate a simple one with the code below, where 32 is the string length.

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 32

Now everything is in place to build the project, this could take a minute as it optimizes the production build. If everything went well, we can also start it to check that command runs successfully too.

npm run build
npm start

If everything has worked so far, you'll see some terminal output showing that it's now running on localhost:3000 (if you're already running something on port 3000 you can change the port by adding PORT=xxx to your .env file and building again).

At this point, everything is running but we can't see the app, and it's not accessible from the outside world. We'll resolve that soon, but first, now we know the app runs correctly, we'll add it as a server Daemon (see below) and Forge will configure Supervisor to make sure it's always running.

Now we'll give it a working web address, so head over to wherever you manage your DNS and create a new A Record for the web address you want to use, stats.yourdomain.com for example.

Next we'll use Forge to create the appropriate starting point Nginx conf...

... and create a LetsEncrypt SSL certificate for us.

Almost there. Finally we will change the Nginx conf to proxy the traffic. Click the Files button at the very bottom of the page, click 'Edit Nginx configuration', and add this location block as shown below.

location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

At long last you should be able to access Umami at https://stats.yourdomain.com now. Login using admin/umami and change your password, then click 'Settings' and create a website to track, save it, and click the </> button to collect the tracking code.

Add the tracking code to your website, visit the website, and return to https://stats.yourdomain.com/realtime where you'll hopefully see your visit in the realtime stats!

And finally

When you release the tracker to your production website or app, you probably don't want your own traffic skewing the data.

On a web app you might want to add the tag in your blade template only when it's in production, and the user is logged in, and isn't an admin (the example assumes you've setup getIsAdminAttribute() on the User model).

@if( config('app.env') == 'production' && Auth::user() && !Auth::user()->is_admin )
<script async defer data-website-id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" src="https://stats.yourdomain.com/umami.js"></script>
@endif

My blog is a static site made with Jigsaw so I'm including the tag only in the production build, and additionally I'm adding the tag dynamically only when a localStorage key called disable-tracking isn't true. This might be overkill for you on your blog, but since mine is quite new, I'm still quite a large proportion of the traffic 😂

@if ( $page->production )
<script>
if(!window.localStorage.getItem('disable-tracking')) {
var script = document.createElement('script');
script.src = "https://stats.yourdomain.com/umami.js"
script.setAttribute('data-website-id', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')
document.getElementsByTagName('head')[0].appendChild(script);
}
</script>
@endif

Going further

The ultimate setup for me would be to use Umami across all sites by hosting that instance on a VM and proxying a sub path (rather than a sub domain) from each site that uses it siteA.com/stats siteB.com/stats through to that instance. I believe this is possible from reading some support threads on GitHub, but I couldn't get it to work immediately and since I don't actually need it at this time. I thought I'd just finish up and spend that time writing this blog post instead.

Joe Vallender photo

Co-founder & CTO at LandlordInvest. Indie hacker. Tech consultant.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Jump24 - UK Laravel Agency

Laravel Developers that Click into Place. Never outsourced. Never offshored. Always exceptional.

Visit Jump24 - UK Laravel Agency
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
The Laravel Community Mobile App Helps You Discover Events and Connect With Developers image

The Laravel Community Mobile App Helps You Discover Events and Connect With Developers

Read article
A PHP Package for Concurrent Website Crawling image

A PHP Package for Concurrent Website Crawling

Read article
A Clean API for Reading PHP Attributes image

A Clean API for Reading PHP Attributes

Read article
Serve Markdown Versions of Your Laravel Pages to AI Agents image

Serve Markdown Versions of Your Laravel Pages to AI Agents

Read article
The Inertia v3 Beta is Here image

The Inertia v3 Beta is Here

Read article
Polyscope Is an Ai-First Dev Environment for Orchestrating Agents image

Polyscope Is an Ai-First Dev Environment for Orchestrating Agents

Read article