From 19cc6e044d7177b938fcc9b47cfe36efb1442d25 Mon Sep 17 00:00:00 2001 From: Joel Helbling Date: Wed, 31 Dec 2025 15:19:25 -0500 Subject: [PATCH 1/3] Add Dockerfile for containerized deployment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Multi-stage build using golang:1.23-alpine and alpine:3.20 - CGO enabled for SQLite support - Includes static files for web UI - Documents Docker build/run in README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Dockerfile | 40 ++++++++++++++++++++++++++++++++++++++++ README.md | 14 ++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..11447c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# Build stage +FROM golang:1.23-alpine AS builder + +# Install build dependencies for CGO (required by SQLite) +RUN apk add --no-cache gcc musl-dev + +WORKDIR /app + +# Copy go mod files first for better layer caching +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build with CGO enabled for SQLite support +RUN CGO_ENABLED=1 go build -ldflags="-s -w" -o minim . + +# Runtime stage +FROM alpine:3.20 + +# Install runtime dependencies for SQLite +RUN apk add --no-cache ca-certificates + +WORKDIR /app + +# Copy binary from builder +COPY --from=builder /app/minim . + +# Copy static files for the web UI +COPY --from=builder /app/static ./static + +# Create data directory +RUN mkdir -p /root/.minim + +# Expose default port +EXPOSE 3333 + +# Run the server directly (execserver runs in foreground) +CMD ["./minim", "execserver"] diff --git a/README.md b/README.md index bbc2f05..83908aa 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,20 @@ Minimalytics is a **standalone minimalist analytics tool** built on SQLite. Desi sudo mv build/minim /usr/local/bin/ ``` +### Docker + +1. Build the image: + ```bash + docker build -t minimalytics . + ``` + +2. Run the container: + ```bash + docker run -p 3333:3333 -v minim-data:/root/.minim minimalytics + ``` + + The volume mount persists the SQLite database across container restarts. + --- ## Usage From 4619be6c585955fb5028019afad3195d58a979b9 Mon Sep 17 00:00:00 2001 From: Joel Helbling Date: Wed, 31 Dec 2025 15:24:24 -0500 Subject: [PATCH 2/3] Add .dockerignore for faster builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Excludes git history, build artifacts, and local config files from Docker build context. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .dockerignore | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7fa1da5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +# Git +.git +.gitignore + +# Build artifacts +build/ +minim + +# Documentation +*.md +docs/ +LICENSE + +# IDE and editor files +.idea/ +.vscode/ +*.swp +*.swo + +# Claude Code +.claude/ + +# Local config +mise.toml From 01ee11357ecde3030765985a39f48f0caebba67d Mon Sep 17 00:00:00 2001 From: Joel Helbling Date: Wed, 31 Dec 2025 15:58:55 -0500 Subject: [PATCH 3/3] Add Docker Compose with HAProxy authentication proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - compose.yml with minimalytics and haproxy services - HAProxy config with basic auth for dashboard, open /api/event/ - Both containers use alpine:3.20 for shared base layers - .env.example with documented configuration options - Updated README with Docker Compose instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .dockerignore | 6 ++++++ .env.example | 30 ++++++++++++++++++++++++++++++ .gitignore | 3 +++ README.md | 25 +++++++++++++++++++++++++ compose.yml | 23 +++++++++++++++++++++++ proxy/Dockerfile | 10 ++++++++++ proxy/haproxy.cfg | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+) create mode 100644 .env.example create mode 100644 compose.yml create mode 100644 proxy/Dockerfile create mode 100644 proxy/haproxy.cfg diff --git a/.dockerignore b/.dockerignore index 7fa1da5..28ed30f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,3 +22,9 @@ LICENSE # Local config mise.toml + +# Docker Compose files (not needed in app image) +compose.yml +.env +.env.example +proxy/ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b937210 --- /dev/null +++ b/.env.example @@ -0,0 +1,30 @@ +# Minimalytics Docker Compose Configuration +# Copy this file to .env and customize the values + +# ============================================================================= +# Port Configuration +# ============================================================================= + +# Internal port for minimalytics (not exposed to host by default) +MINIM_PORT=3333 + +# External port for the authenticated proxy (exposed to host) +PROXY_PORT=3334 + +# ============================================================================= +# Authentication +# ============================================================================= + +# Username for dashboard access +AUTH_USER=admin + +# Password for dashboard access (change this!) +AUTH_PASS=changeme + +# ============================================================================= +# Optional: Domain Configuration +# ============================================================================= + +# Your domain name (used for logging/identification, not for TLS) +# TLS termination is handled by your production reverse proxy +DOMAIN=localhost diff --git a/.gitignore b/.gitignore index b5215a6..3165f27 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ templates/* .DS_Store build/* + +# Environment file with credentials +.env diff --git a/README.md b/README.md index 83908aa..ebf82bb 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,31 @@ Minimalytics is a **standalone minimalist analytics tool** built on SQLite. Desi The volume mount persists the SQLite database across container restarts. +### Docker Compose (with authentication) + +Docker Compose runs minimalytics with an HAProxy reverse proxy that adds basic authentication to the dashboard while leaving the event API open. + +1. Copy the example environment file and configure: + ```bash + cp .env.example .env + ``` + +2. Edit `.env` with your credentials: + ``` + AUTH_USER=admin + AUTH_PASS=your-secure-password + PROXY_PORT=3334 + ``` + +3. Start the services: + ```bash + docker compose up -d + ``` + +4. Access the services: + - **Dashboard** (requires auth): `http://localhost:3334` + - **Event API** (no auth): `http://localhost:3334/api/event/` + --- ## Usage diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..3188e62 --- /dev/null +++ b/compose.yml @@ -0,0 +1,23 @@ +services: + minimalytics: + build: . + environment: + - MINIM_PORT=${MINIM_PORT:-3333} + volumes: + - minim-data:/root/.minim + restart: unless-stopped + + haproxy: + build: ./proxy + ports: + - "${PROXY_PORT:-3334}:80" + environment: + - AUTH_USER=${AUTH_USER:-admin} + - AUTH_PASS=${AUTH_PASS:-changeme} + - MINIM_PORT=${MINIM_PORT:-3333} + depends_on: + - minimalytics + restart: unless-stopped + +volumes: + minim-data: diff --git a/proxy/Dockerfile b/proxy/Dockerfile new file mode 100644 index 0000000..63e90ef --- /dev/null +++ b/proxy/Dockerfile @@ -0,0 +1,10 @@ +# Use same Alpine version as minimalytics for layer sharing +FROM alpine:3.20 + +RUN apk add --no-cache haproxy + +COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg + +EXPOSE 80 + +CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"] diff --git a/proxy/haproxy.cfg b/proxy/haproxy.cfg new file mode 100644 index 0000000..6a3d6bc --- /dev/null +++ b/proxy/haproxy.cfg @@ -0,0 +1,32 @@ +global + log stdout format raw local0 + +defaults + log global + mode http + option httplog + timeout connect 5s + timeout client 30s + timeout server 30s + +userlist auth_users + user "${AUTH_USER}" insecure-password "${AUTH_PASS}" + +frontend http_front + bind *:80 + + # ACL for event API - no auth required + acl is_event_api path_beg /api/event/ + + # Route event API without auth + use_backend minimalytics_noauth if is_event_api + + # Everything else requires auth + default_backend minimalytics_auth + +backend minimalytics_noauth + server app minimalytics:"${MINIM_PORT}" check + +backend minimalytics_auth + http-request auth unless { http_auth(auth_users) } + server app minimalytics:"${MINIM_PORT}" check