docker-quickstart.html
Rama main · réplica de lectura del repositorio original.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="canonical" href="https://newtondarts.com/docker-quickstart.html">
<title>Docker Quick Start — NewTon DC Tournament Manager</title>
<meta name="description" content="Self-host NewTon DC Tournament Manager with Docker in under 2 minutes. Includes docker-compose.yml, environment variables, reverse proxy setup, and persistent storage configuration.">
<link rel="icon" type="image/jpeg" href="images/logo.jpg">
<link rel="preload" href="fonts/Insignia-Regular.otf" as="font" type="font/otf" crossorigin>
<link rel="preload" href="fonts/Inter-VariableFont_opsz,wght.ttf" as="font" type="font/ttf" crossorigin>
<link rel="stylesheet" href="css/landing.css">
</head>
<body>
<a class="docs-back-link" href="/">← NewTon DC Tournament Manager</a>
<div class="docs-content">
<h1>Docker Quick Start</h1>
<p class="docs-subtitle">Get NewTon DC Tournament Manager running in Docker in under 2 minutes.</p>
<nav class="docs-toc">
<h2>Contents</h2>
<ol>
<li><a href="#quick-start">Quick Start</a></li>
<li><a href="#setup-options">Setup Options</a>
<ol>
<li><a href="#ssl-all-platforms">SSL (All Platforms)</a></li>
<li><a href="#reverse-proxy">Behind a Reverse Proxy</a></li>
</ol>
</li>
<li><a href="#configuration">Environment Variables</a></li>
<li><a href="#security">Security</a></li>
<li><a href="#customization">Customization</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ol>
</nav>
<h2 id="quick-start">Quick Start</h2>
<p><strong>Prerequisites:</strong> Docker and Docker Compose installed. <strong>Don't have Docker?</strong> <a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noopener">Download Docker Desktop</a> (macOS/Windows) or install via your package manager (Linux).</p>
<div class="terminal-wrap">
<div class="terminal-bar"></div>
<pre><code>mkdir newton-tournament
cd newton-tournament
curl -O https://raw.githubusercontent.com/skrodahl/NewTon/main/docker/docker-compose.yml
docker compose up -d</code></pre>
</div>
<p>Open <code>http://localhost:2020</code> — the Tournament Manager is running.</p>
<p><strong>Port 2020 = “Double 20”</strong> — nginx runs on port 2020 internally. The compose file publishes the same port on the host.</p>
<p><strong>Camera / QR scanning requires HTTPS.</strong> The quick start runs HTTP only. Pick one of the three setups below to enable camera access.</p>
<hr>
<h2 id="setup-options">Setup Options</h2>
<blockquote>
<p><strong>Image tag:</strong> All compose files below use <code>skrodahl/newton:latest</code> (Docker Hub) with GHCR as a commented alternative.</p>
</blockquote>
<h3 id="ssl-all-platforms">SSL (All Platforms)</h3>
<p>SSL enabled by default — gives you HTTPS and camera access on any platform.</p>
<pre><code>services:
newton-tournament:
image: skrodahl/newton:latest
# Alternative: ghcr.io/skrodahl/newton:latest
container_name: newton
ports:
- "2020:2020" # HTTP — redirects to HTTPS
- "443:443" # HTTPS
volumes:
- ./tournaments:/var/www/html/tournaments
- ./images:/var/www/html/images:ro
- newton-ssl:/etc/nginx/ssl # Persists the auto-generated certificate
restart: unless-stopped
environment:
- TZ=Europe/Oslo
- SSL_ENABLED=true # Auto-generates a self-signed cert (30-year, SAN)
- NEWTON_API_ENABLED=true # Set false to disable tournament upload/download
- NEWTON_DEMO_MODE=false # Set true to show a privacy banner
# - NEWTON_LANDING_PAGE=true # Show landing page at root URL
# - NEWTON_BASE_URL=https://darts.example.com
volumes:
newton-ssl:</code></pre>
<p>Save as <code>docker-compose.yml</code> in a folder of your choice, then run <code>docker compose up -d</code>. That’s it.</p>
<p>Access at <code>https://localhost</code>. Your browser will warn about the self-signed certificate on first visit — accept the exception once and it will not appear again.</p>
<h4>LAN Access via <code><hostname>.local</code></h4>
<p>Phones, tablets, and scoring stations on the same network can reach the container by name (e.g. <code>https://newtondarts.local</code>) once mDNS is set up on the host. macOS has Bonjour built in; Linux needs avahi-daemon; Windows needs Bonjour installed. See <a href="https://github.com/skrodahl/NewTon/blob/main/Docs/MDNS.md" target="_blank" rel="noopener">Docs/MDNS.md</a> for full setup steps.</p>
<hr>
<h3 id="reverse-proxy">Behind a Reverse Proxy</h3>
<p>The container joins your proxy’s Docker network. SSL termination and HTTPS are handled by the proxy — no <code>SSL_ENABLED</code> needed.</p>
<pre><code>services:
newton-tournament:
image: skrodahl/newton:latest
# Alternative: ghcr.io/skrodahl/newton:latest
container_name: newton
# ports:
# - "2020:2020" # Remove when using a reverse proxy
networks:
- proxy_network
volumes:
- ./tournaments:/var/www/html/tournaments
- ./images:/var/www/html/images:ro
restart: unless-stopped
environment:
- TZ=Europe/Oslo
- NEWTON_API_ENABLED=true
- NEWTON_DEMO_MODE=false
# - NEWTON_LANDING_PAGE=true
# - NEWTON_BASE_URL=https://darts.example.com
networks:
proxy_network:
external: true</code></pre>
<p>Save as <code>docker-compose.yml</code> in a folder of your choice, then run <code>docker compose up -d</code>. That’s it.</p>
<p>In Nginx Proxy Manager, point the proxy host to <code>newton:2020</code>.</p>
<h4>Camera Access Behind NPM</h4>
<p>NPM sets its own <code>Permissions-Policy</code> header that overrides the container’s, blocking camera access in the Chalker. Add this to the <strong>Advanced</strong> tab of your NPM proxy host:</p>
<pre><code>more_set_headers "Permissions-Policy: geolocation=(), microphone=(), camera=(self), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), ambient-light-sensor=()";</code></pre>
<p><code>more_set_headers</code> replaces the header rather than adding a duplicate. OpenResty (which NPM runs on) includes the <code>headers-more</code> module, so this directive is available without any additional setup.</p>
<h4>Demo / Public Site</h4>
<pre><code>environment:
- NEWTON_API_ENABLED=false # Disable upload API for security
- NEWTON_DEMO_MODE=true # Show privacy banner
- NEWTON_LANDING_PAGE=true # Show landing page at root</code></pre>
<h4>Analytics Instance</h4>
<p>A read-only analytical surface — browse leaderboards, match history, and brackets. Tournament data comes from shared tournament files on disk. No tournament management tabs, no deletion. Accepts tournament uploads from venue instances via the relay API.</p>
<pre><code>environment:
- NEWTON_API_ENABLED=true # Required for auto-import and bracket view
- NEWTON_MODE=analytics # Analytics-only mode</code></pre>
<p>Mount the <code>./tournaments</code> and <code>./images</code> volumes. Configure the venue instance to auto-backup to this server’s relay API, or share the same tournaments directory. See <a href="automatic-backup.html">Automatic Backup</a> for the full setup guide.</p>
<hr>
<h2 id="configuration">Environment Variables</h2>
<table>
<thead>
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>SSL_ENABLED</code></td><td><code>false</code></td><td>Auto-generates a self-signed certificate; enables HTTPS on <code>HTTPS_PORT</code> and redirects HTTP on <code>HTTP_PORT</code></td></tr>
<tr><td><code>HTTP_PORT</code></td><td><code>2020</code></td><td>HTTP listening port (redirects to HTTPS when SSL enabled)</td></tr>
<tr><td><code>HTTPS_PORT</code></td><td><code>443</code></td><td>HTTPS listening port</td></tr>
<tr><td><code>SSL_HOSTNAME</code></td><td><code>newtondarts</code></td><td>Hostname used in the auto-generated SSL certificate’s Subject Alternative Name. Set this to match the <code>.local</code> hostname you advertise via mDNS — see <a href="https://github.com/skrodahl/NewTon/blob/main/Docs/MDNS.md" target="_blank" rel="noopener">Docs/MDNS.md</a>. Falls back to deprecated <code>MDNS_HOSTNAME</code> if unset.</td></tr>
<tr><td><code>NEWTON_API_ENABLED</code></td><td><code>true</code></td><td>Enables REST API endpoints for tournament upload, download, and delete</td></tr>
<tr><td><code>NEWTON_MODE</code></td><td><code>full</code></td><td>App mode. <code>analytics</code> hides tournament management tabs, shows only Analytics and limited Global Settings</td></tr>
<tr><td><code>NEWTON_API_KEY</code></td><td><em>unset</em></td><td>Key required (as the <code>X-API-Key</code> header) for uploads, deletes, corrections and the relay. Reading never needs it. Unset = no key required</td></tr>
<tr><td><code>NEWTON_DEMO_MODE</code></td><td><code>false</code></td><td>Shows a privacy banner at the top of the app</td></tr>
<tr><td><code>NEWTON_LANDING_PAGE</code></td><td><code>false</code></td><td>Shows a landing page at the root URL instead of loading the app directly</td></tr>
<tr><td><code>NEWTON_BASE_URL</code></td><td><em>unset</em></td><td>Canonical URL for Open Graph and Twitter Card meta tags on the landing page</td></tr>
<tr><td><code>NEWTON_GITHUB_URL</code></td><td><code>github.com/skrodahl/NewTon</code></td><td>GitHub link shown in the demo banner</td></tr>
</tbody>
</table>
<hr>
<h2 id="security">Security</h2>
<h3>REST API</h3>
<p><strong>Without <code>NEWTON_API_KEY</code>, the REST API has no authentication.</strong> Do not expose the container directly to the public internet without additional protection. Safe options:</p>
<ul>
<li>✅ <strong>Local network only</strong> — home or club WiFi, default setup is fine</li>
<li>✅ <strong>Localhost only</strong> — by default, Docker binds to <code>0.0.0.0</code>, meaning the container is reachable from any device on your network. To restrict access to the local machine only, specify the loopback address explicitly in your <code>ports</code> mapping:
<pre><code>ports:
- "127.0.0.1:2020:2020"</code></pre>
With this in place, <code>http://localhost:2020</code> still works, but other devices on the network cannot reach the container.
</li>
<li>✅ <strong>Behind VPN</strong> — Tailscale, WireGuard, or similar</li>
<li>✅ <strong>API key for writes</strong> — set <code>NEWTON_API_KEY</code>: uploading or deleting a tournament, saving corrections and the relay need it (as the <code>X-API-Key</code> header); reading never does. This is the way to run a public analytics-only instance that members open without a login. The venue enters the same key under Global Settings → Server & backup → Remote backup, and <code>?tm</code> with the correct <code>NEWTON_TM_PASSWORD</code> works as before. Network handover is switched off on an analytics-only instance.</li>
<li>✅ <strong>Reverse proxy with auth</strong> — nginx or Caddy with HTTP basic authentication (members then need the login too)</li>
<li>✅ <strong>Disable API</strong> — set <code>NEWTON_API_ENABLED=false</code></li>
</ul>
<h3>Security Headers</h3>
<p>The Docker image includes comprehensive security headers enabled by default — no configuration needed:</p>
<ul>
<li><strong>X-Frame-Options: SAMEORIGIN</strong> — Prevents clickjacking</li>
<li><strong>X-Content-Type-Options: nosniff</strong> — Prevents MIME-type sniffing</li>
<li><strong>Referrer-Policy: strict-origin-when-cross-origin</strong> — Limits referrer data leakage</li>
<li><strong>Permissions-Policy</strong> — Restricts camera and other device access to the app origin</li>
<li><strong>Content-Security-Policy</strong> — Prevents loading external resources</li>
<li><strong>Server tokens hidden</strong> — nginx and PHP versions not disclosed</li>
</ul>
<p>NewTon DC Tournament Manager achieves an <strong>A grade</strong> on <a href="https://securityheaders.com" target="_blank" rel="noopener">securityheaders.com</a> out of the box.</p>
<h3>HSTS</h3>
<p>Strict-Transport-Security is included automatically when <code>SSL_ENABLED=true</code>. It is not set in HTTP-only mode to avoid breaking non-SSL deployments.</p>
<p>When terminating HTTPS at a reverse proxy, add HSTS there instead:</p>
<pre><code># Nginx Proxy Manager — Advanced tab
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;</code></pre>
<hr>
<h2 id="customization">Customization</h2>
<h3>Logo and Payment QR Code</h3>
<p>Place your own files in the <code>images/</code> folder. The <code>images/</code> volume is mounted read-only from the host, so changes are picked up immediately — no container restart needed.</p>
<div class="terminal-wrap">
<div class="terminal-bar"></div>
<pre><code>mkdir -p images
# Custom logo — supports .png, .jpg, .jpeg, or .svg
cp /path/to/your/logo.jpg ./images/logo.jpg
# Custom payment QR code
cp /path/to/your/payment-qr.png ./images/payment.png</code></pre>
</div>
<p>Default images bundled in the Docker image: <code>images/logo.jpg</code> (club logo placeholder) and <code>images/payment.png</code> (GitHub project QR code). Need a payment QR? Use the <a href="qr-generator.html">QR Code Generator</a> — transparent background, no signup required.</p>
<hr>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3>Port Already in Use</h3>
<p>Change the host port (the first number) in <code>docker-compose.yml</code>, then open <code>http://localhost:9000</code>:</p>
<pre><code>ports:
- "9000:2020"</code></pre>
<h3>Container Won’t Start</h3>
<div class="terminal-wrap">
<div class="terminal-bar"></div>
<pre><code>docker compose logs</code></pre>
</div>
<h3>Can’t Access http://localhost:2020</h3>
<ol>
<li>Verify the container is running: <code>docker compose ps</code> — should show <code>0.0.0.0:2020->2020/tcp</code></li>
<li>Try <code>http://127.0.0.1:2020</code> instead of <code>localhost</code></li>
<li>Check logs: <code>docker compose logs</code></li>
</ol>
<h3>Browser Warning on https://localhost</h3>
<p>Expected with a self-signed certificate. Accept the exception once — it will not appear again. The cert is stored in the <code>newton-ssl</code> named volume and persists across restarts.</p>
<h3><code><hostname>.local</code> Not Reachable</h3>
<p>mDNS resolution is a host-side concern, not handled by the container. See <a href="https://github.com/skrodahl/NewTon/blob/main/Docs/MDNS.md" target="_blank" rel="noopener">Docs/MDNS.md</a> for full setup. Quick checks:</p>
<ul>
<li><strong>macOS:</strong> Bonjour is built in — should just work</li>
<li><strong>Windows:</strong> install Bonjour</li>
<li><strong>Linux:</strong> install and start <code>avahi-daemon</code> on the host, set hostname, and configure <code>allow-interfaces</code> to exclude Docker bridge interfaces</li>
<li>Confirm resolution from another device on the LAN: <code>ping newtondarts.local</code></li>
</ul>
<hr>
<h2>Need Help?</h2>
<ul>
<li><strong>Application help:</strong> Press F1 inside the tournament manager</li>
<li><strong>REST API reference:</strong> <a href="rest-api.html">REST API Documentation</a></li>
<li><strong>QR protocol:</strong> <a href="qr-protocol.html">QR Code Communication Protocol</a></li>
<li><strong>Privacy model:</strong> <a href="privacy.html">Privacy Architecture</a></li>
<li><strong>Issues & feedback:</strong> <a href="https://github.com/skrodahl/NewTon/issues" target="_blank" rel="noopener">GitHub Issues</a></li>
</ul>
</div>
<!-- Footer -->
<div class="landing-footer docs-footer">
<p>
NewTon DC Tournament Manager — BSD-3-Clause License —
<a href="https://github.com/skrodahl/NewTon" target="_blank" rel="noopener">GitHub</a> —
<a href="privacy.html">Privacy</a>
</p>
<p><em class="footer-cheeky">No popups? No cookies!</em></p>
</div>
</body>
</html>