What You'll Build
A self-hosted Umami analytics dashboard with PostgreSQL - running on your machine in under 5 minutes. No Google Analytics, no data leaves your server.
- Umami dashboard (Node.js)
- PostgreSQL database with healthchecks
- Persistent volumes for data
- .env template with auto-generated secrets
Quick Answer
mkdir ~/umami && cd ~/umami
stacker init --from-github umami-software/umami --with-ai --force
cp .env.example .env && ./scripts/generate-secrets.sh
stacker deploy --target local
Dashboard at http://localhost:3000. Default login: admin / umami.
Prerequisites
- Docker (
docker --version) - Git (
git --version) - Stacker (
stacker --version)
Step 1: Install Stacker
curl -fsSL https://raw.githubusercontent.com/trydirect/stacker/main/install.sh | bash
stacker --version
# stacker 0.3.0
Step 2: Generate Configuration
mkdir ~/umami && cd ~/umami
stacker init --from-github umami-software/umami --with-ai --force
Stacker clones Umami, reads the docker-compose.yml, and generates:
stacker.yml # Deployment config
.stacker/Dockerfile # Optimized Node.js build
.stacker/docker-compose.yml # Production compose file
.env.example # All env vars with placeholders
scripts/generate-secrets.sh # Fills in passwords
Step 3: Configure Secrets
cp .env.example .env
./scripts/generate-secrets.sh
# Generates: DB_PASSWORD, DATABASE_URL, HASH_SALT
Optional - edit .env to set your timezone or tracking domain:
TZ=UTC
TRACKER_SCRIPT_NAME=tracker.js
Step 4: Deploy
stacker config validate
# ✓ Configuration is valid
stacker deploy --target local
# Building app...
# Creating postgres...
# ✓ Stack deployed successfully
Step 5: Verify and Use
stacker status
# umami running
# postgres running
open http://localhost:3000
Login with admin / umami. Change the password immediately.
Understanding the Generated Config
The stacker.yml that Stacker generated:
name: umami
app:
type: node
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgresql://umami:${DB_PASSWORD}@postgres:5432/umami"
NEXT_AUTH_URL: "http://localhost:3000"
NEXT_AUTH_SECRET: "${HASH_SALT}"
services:
- name: postgres
image: postgres:16-alpine
ports:
- "127.0.0.1:5432:5432"
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: "${DB_PASSWORD}"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: "CMD pg_isready -U umami"
interval: 5s
timeout: 2s
retries: 10
volumes:
postgres_data: {}
What Stacker got right automatically:
- Node.js detected from
package.json - PostgreSQL service extracted from compose file
- Healthcheck -
pg_isreadyadded for database - Port isolation - PostgreSQL on
127.0.0.1only - Named volume - data persists across restarts
- Secrets - no hardcoded passwords
Customizing
Add a reverse proxy with SSL
# Add to stacker.yml:
proxy:
type: nginx
auto_detect: true
domains:
- domain: analytics.yourdomain.com
ssl: auto
upstream: app:3000
stacker deploy --target local
Deploy to a remote server
stacker config setup server
# Enter: host, user, SSH key
stacker deploy --target server
Deploy to cloud
stacker config setup cloud
# Choose: hetzner, digitalocean, aws, linode, vultr
stacker deploy --target cloud
Adding Your Website to Umami
Once Umami is running:
- Open
http://localhost:3000 - Login → Settings → Websites → Add Website
- Enter your domain and name
- Copy the tracking script into your HTML:
<script defer src="http://localhost:3000/tracker.js"
data-website-id="your-website-id"></script>
Troubleshooting
Port 3000 already in use
# Edit stacker.yml:
app:
ports:
- "3001:3000"
stacker deploy --target local
Database connection refused
# Check PostgreSQL is healthy:
stacker status
# Check logs:
stacker logs postgres
Want to reset everything
stacker destroy --target local
rm -rf .stacker stacker.yml .env .env.example
Key Takeaways
- Umami deploys in 5 minutes with 4 commands
- Stacker handles the entire Docker stack automatically
- Healthchecks, port isolation, and secret generation are built in
- The same config works on local, server, or cloud
- Source: github.com/trydirect/stacker