Quick start
Change two paths. Deploy the stack.
The supplied stack already contains the public image and safe defaults. For a normal installation, the only required edits are the two host-side folder paths.
/srv/needle-web/dataDatabase, backups, artwork, logs, and generated streaming files.
/media/musicThe folder that contains the music After2AM Audio Server should index.
- Download the stack.Save the supplied YAML or paste it into Portainer’s stack web editor.
- Name the stack.Use
needle-web, or another name you will recognize. - Change the two paths.Edit only the left side of the two volume mappings for your first deployment.
- Deploy and open it.Deploy the stack, then visit
http://<server-address>:2104. - Finish setup.Create the first account, confirm the library folder, and start the initial scan.
Port 2104 is for your local network. Never forward it—or container port 3000—directly from the router.
Storage
Your two folders
Docker sees the path on the right; your server sees the path on the left. Keep the container paths unchanged unless you are deliberately building a more advanced layout.
volumes:
# Change the host-side paths on the left.
- /srv/needle-web/data:/app/data
- /media/music:/music
/app/data
Must survive container replacement. It holds the account, library database, settings, backups, artwork cache, Managed HTTPS state, and streaming derivatives.
/music
Mounted writable by default so supported tag edits can be saved to the files. Originals are not replaced when streaming copies are generated.
Networking
Three ports, three different jobs
| Port | Purpose | What to do |
|---|---|---|
2104 | Default host port for direct LAN access. | Open this from trusted devices at home. You may change the host side of the mapping. |
3000 | Internal HTTP port used by the container. | Leave it unchanged and never forward it from the router. |
8443 | Dormant Managed HTTPS listener. | Leave it alone unless enabling Managed HTTPS or resolving a host-port conflict. |
ports:
- "2104:3000/tcp"
- "${NEEDLE_MANAGED_HTTPS_PORT:-8443}:${NEEDLE_MANAGED_HTTPS_PORT:-8443}/tcp"
The first number is the port on your Docker host. The second is the port inside the container. Changing 2104:3000 to 3001:3000 moves the local address to port 3001 without changing the server inside the container.
Reference
Every option in the supplied YAML
The defaults are intentional. Settings marked “leave” normally need no attention; they are documented here so the file is understandable rather than mysterious.
Container settings
| Setting | Supplied value | Meaning |
|---|---|---|
services: needle-web | needle-web | The Compose service name used by Portainer. |
image | davidrelich/needle-web:latest | The public Docker image Portainer pulls when the stack is deployed or updated. |
container_name | needle-web | A predictable container name for Portainer, logs, and maintenance. |
restart | unless-stopped | Restarts the server after a crash or host reboot unless you deliberately stopped it. |
ports | 2104 and 8443 | Publishes the LAN server port and the dormant Managed HTTPS listener described above. |
volumes | /app/data and /music | Connects persistent server data and your music library to the container. |
Core server settings
| Option | Default | Meaning |
|---|---|---|
PORT | 3000 | Internal server port. Leave unchanged. |
NEEDLE_LIBRARY_ROOTS | /music | One or more in-container folders to index. Separate multiple roots with commas. |
NEEDLE_WRITE_BACK_ENABLED | true | Allows supported Rating, Tags, BPM, and album Genre edits to be written to compatible files. |
NEEDLE_LOG_LEVEL | info | Normal operational logging. Change only while diagnosing a problem. |
Authentication and request boundaries
| Option | Default | Meaning |
|---|---|---|
NEEDLE_SECURE_COOKIES | auto | Uses secure cookies over HTTPS while preserving direct LAN login over HTTP. Recommended for the supplied stack. |
NEEDLE_TRUSTED_PROXIES | blank | Exact proxy IP addresses or narrow CIDRs. Leave blank unless the backend is private behind a configured external proxy. |
NEEDLE_ALLOWED_HOSTS | blank | Additional public or proxy hostnames. Normal IP, localhost, .local, and .home.arpa access needs no entry. |
NEEDLE_ALLOWED_ORIGINS | blank | Extra origins permitted for browser mutations. Same-origin installations leave this blank. |
NEEDLE_SETUP_TOKEN | blank | Optional one-time secret for advanced initial setup through an existing proxy. Direct LAN setup does not need it. |
NEEDLE_AUTH_JSON_BODY_LIMIT | 16kb | Maximum setup and login request size. Leave unchanged. |
NEEDLE_DEFAULT_JSON_BODY_LIMIT | 256kb | Maximum ordinary authenticated mutation size. Leave unchanged. |
NEEDLE_MEDIUM_JSON_BODY_LIMIT | 2mb | Limit for playlists, playback state, Collection items, and desktop metadata payloads. |
NEEDLE_ARTIST_IMAGE_JSON_BODY_LIMIT | 17mb | Allows bounded manual artist-image uploads. Decoded images are capped separately. |
NEEDLE_JSON_BODY_LIMIT | 50mb | Limit for authenticated desktop-state bulk imports. Leave unchanged. |
Streaming files
| Option | Default | Meaning |
|---|---|---|
NEEDLE_STREAMING_DERIVATIVES_ENABLED | true | Generates AAC-LC copies for predictable remote and mobile streaming without transcoding during playback. |
NEEDLE_STREAMING_DERIVATIVE_DIR | /app/data/streaming-cache | Persistent location for generated streaming files. Leave inside /app/data. |
NEEDLE_STREAMING_LOUDNESS_NORMALIZATION | true | Measures source loudness and applies controlled gain to generated copies. Originals remain untouched. |
NEEDLE_STREAMING_LOUDNESS_MODE | album | Preserves the relative dynamics between tracks on the same album. |
NEEDLE_STREAMING_TARGET_LUFS | -16 | Target loudness baked into generated streaming copies. |
NEEDLE_STREAMING_TRUE_PEAK_DB | -1.5 | Peak-protection ceiling used while normalizing streaming copies. |
NEEDLE_DERIVATIVE_CONCURRENCY | 2 | Maximum concurrent FFmpeg jobs. Lower it on resource-constrained hardware. |
NEEDLE_DERIVATIVE_MIN_FREE_GB | 10 | Stops generation before the target filesystem would fall below this free-space reserve. |
NEEDLE_DERIVATIVE_MIN_FREE_PERCENT | 10 | Adds a percentage-based reserve for shared or system disks. |
Managed HTTPS helper
| Option | Default | Meaning |
|---|---|---|
NEEDLE_MANAGED_CADDY_BIN | /usr/bin/caddy | Bundled Caddy executable. Leave unchanged. |
NEEDLE_MANAGED_CADDY_DIR | /app/data/managed-caddy | Persistent certificates, configuration, and logs. |
NEEDLE_MANAGED_CADDY_RUNTIME_DIR | /tmp/needle-managed-caddy | Temporary runtime state inside the container. |
NEEDLE_MANAGED_HTTPS_PORT | 8443 | Local listener targeted by WAN TCP 443 only after Managed HTTPS is enabled. |
Variations
Three common changes
Make the music library read-only
Change both the write-back setting and the volume mount so the intent is explicit.
- NEEDLE_WRITE_BACK_ENABLED=false
volumes:
- /media/music:/music:ro
Use another LAN port
Change only the host side. This example moves the local address to http://<server-address>:3001.
ports:
- "3001:3000/tcp"
Use several mounted music folders
Mount each host folder under a distinct container path, then list those container paths as comma-separated roots.
environment:
- NEEDLE_LIBRARY_ROOTS=/music/main,/music/vinyl
volumes:
- /media/music:/music/main
- /media/vinyl-rips:/music/vinyl
Remote access
Local first. Public only by choice.
Deploying the stack does not expose the library to the Internet. The Caddy binary and port mapping remain dormant until an authenticated user explicitly enables Managed HTTPS in After2AM Audio Server Settings.
Do not router-forward host port 2104, container port 3000, TCP 80, or UDP 443. Managed HTTPS needs only WAN TCP 443 forwarded to the configured local managed-HTTPS port.
Managed HTTPS
- Complete first-account setup over the local network.
- Prepare a real domain, a direct DNS record, a public ISP address, and a stable LAN address for the Docker host.
- Forward WAN TCP
443to host TCP8443, unless you deliberately changed the managed port. - Open Settings → Remote access → Managed HTTPS and complete the guided setup.
- Run the built-in checks, then test from a genuinely external connection.
If the ISP uses CGNAT, ordinary inbound forwarding will not work. Request a public address or use a suitable VPN or tunnel instead.
Existing HTTPS proxy
This is an advanced alternative. Keep the backend private, configure only the exact immediate proxy address in NEEDLE_TRUSTED_PROXIES, and add the public hostname to NEEDLE_ALLOWED_HOSTS. The proxy must preserve the original Host and overwrite incoming forwarding headers. The Remote access panel generates deployment-specific instructions.
Keep it healthy
Updates and troubleshooting
Keep /app/data
Updating or recreating the container is safe when the host data folder remains mounted to /app/data.
Check the mount
Confirm the host music path exists, Docker can read it, and NEEDLE_LIBRARY_ROOTS names the container-side path.
Check both switches
Write-back needs a writable music mount and NEEDLE_WRITE_BACK_ENABLED=true.
Use cookie mode auto
A forced secure cookie requires HTTPS. The supplied auto mode supports both direct LAN HTTP and configured HTTPS.
Check free space
The data volume must be writable and remain above both configured free-space reserves.
Check Docker Hub access
The host must be able to pull davidrelich/needle-web:latest before Portainer can deploy it.
Ready to deploy
Keep the defaults. Change your paths.
That is enough for a normal local installation. Return here only when you need a deliberate variation.