Stop Wrestling With FFmpeg! TinyIce Streams Everything in One Go Binary
What if your streaming infrastructure didn't need a fragile Rube Goldberg machine of FFmpeg processes, nginx-rtmp modules, and Node.js transcoders held together with cron jobs and prayer? What if you could drop a single binary onto any server—ARM, x86, Docker↗ Bright Coding Blog, bare metal—and have professional-grade audio and video streaming with HLS output, WebRTC playback, AutoDJ, and an admin dashboard that actually looks like it was built this decade?
The secret weapon top broadcast engineers are quietly adopting isn't another patchwork of shell scripts. It's TinyIce—a pure-Go streaming server that speaks Icecast2, RTMP, SRT, and WebRTC fluently, then outputs HLS, WHEP, and classic Icecast streams without breaking a sweat. No FFmpeg dependency. No runtime surprises. One 25 MB static binary that scales to six figures of concurrent listeners.
If you're still chaining together ffmpeg -re -i pipelines that collapse at 3 AM, this changes everything.
What Is TinyIce?
TinyIce is an Icecast2-compatible streaming server written entirely in Go by DatanoiseTV. Born from the frustration of maintaining complex media stacks, it reimagines broadcast infrastructure as a single, self-contained deployment that handles ingest, transcoding, output, and management without external dependencies.
The project exploded in relevance as developers discovered what "pure-Go" actually means for operations: no shared library hell, no version conflicts between FFmpeg builds, and cross-compilation that actually works. While traditional streaming setups require careful orchestration of multiple services—often with conflicting codec support and brittle inter-process communication—TinyIce embeds everything from H.264 parsing to Opus transcoding directly into its binary.
The "tiny" in TinyIce is deliberate misdirection. The binary is small (~25 MB with all web assets embedded), but the capability footprint is massive. It ingests via Icecast SOURCE/PUT, RTMP, SRT MPEG-TS, WebRTC browser broadcasting, and Icecast relay pull. It outputs Icecast passthrough, HLS audio, HLS audio+video, WHEP/WebRTC playback, and even OBS simulcast master playlists. The built-in transcoder handles MP3, Ogg Opus, Ogg Vorbis, FLAC, WAV, and resamples automatically—no FFmpeg installation required.
What makes TinyIce genuinely disruptive is how it collapses the traditional separation between "audio streaming server" and "video streaming server." Icecast historically handled audio brilliantly but video poorly or not at all. nginx-rtmp and SRS excel at video but treat audio as a second-class citizen. TinyIce treats both as first-class, unified under one configuration format, one admin interface, and one operational model.
Key Features That Destroy the Competition
Multi-Protocol Ingest Without Compromise
TinyIce accepts streams from virtually every broadcast tool ever made. OBS Studio pushes RTMP H.264 + AAC directly. Professional encoders feed SRT MPEG-TS. Browser-based guests broadcast via WebRTC. Classic audio tools like BUTT, Mixxx, and LadioCast use standard Icecast SOURCE. Even FFmpeg's icecast:// protocol works natively. Each protocol feeds into the same unified pipeline—no separate "audio mount" and "video application" concepts to manage.
Pure-Go Transcoding: The FFmpeg Killer
Here's where engineers do a double-take. TinyIce's transcoder decodes MP3, Ogg Opus, Ogg Vorbis, FLAC, FLAC-in-Ogg, and WAV (8/16/24/32-bit PCM and IEEE float, mono or stereo) entirely in Go. It resamples automatically when targeting Opus's locked 48 kHz sample rate—no more 8.8% speedup bugs from mismatched rates. The MP3 encoder actually honors bitrate settings. For operators who've debugged ffmpeg processes consuming 4GB RAM and crashing on malformed Ogg pages, this is liberation.
HLS A/V with Sub-Second Latency Options
Default HLS segments dropped from 4 seconds to 1 second in v2.1.0, with keyframe-aligned flushes that maintain clean IDR boundaries even when encoder GOP exceeds segment duration. For interactive use cases, WHEP (WebRTC Egress Protocol) delivers sub-second latency. The built-in player auto-detects video mounts, switches to 16:9 layout, and overlays real-time stats: codec, resolution, FPS, GOP size, bitrate, buffer depth, dropped frames, and live-edge latency.
AutoDJ with MPD Protocol Compatibility
Run unattended radio with multi-instance AutoDJ supporting shuffle, loop, queue modes, and keyframe-accurate pacing. Control via standard MPD protocol—any MPD client works. Inject metadata automatically. Hook external song_command scripts for dynamic playlist generation, or on_play_command for per-track notifications. This isn't a toy; it's production radio automation.
Enterprise Authentication & Security
Username/password with bcrypt, WebAuthn passkeys, OIDC/OAuth2 single sign-on, and per-mount source passwords. Bearer API tokens for automation. CSRF protection on all admin mutations. SSRF prevention on webhooks and relays. Constant-time login to prevent timing attacks. Sessions with absolute 7-day and sliding 24-hour expiry, immediate invalidation on user deletion.
Observability That Doesn't Suck
Prometheus metrics at /metrics with per-mount listener counts, bytes in/out, memory, goroutines, GC stats. Structured JSON logs for ELK/Loki ingestion. Separate auth audit trail. Server-sent events for live dashboard updates. A Grafana dashboard ships in the repository.
Real-World Use Cases Where TinyIce Dominates
Internet Radio Station Modernization
Legacy Icecast setups work until you need video, HLS for mobile apps, or modern authentication. TinyIce replaces the entire stack: keep your existing Icecast sources, add HLS output for iOS/Android apps, enable AutoDJ for overnight programming, and authenticate DJs via OIDC instead of sharing one password. The admin SPA means non-technical staff can manage streams without SSH access.
Live Event Streaming with Minimal Crew
One operator, one server, multiple camera feeds. OBS outputs RTMP to TinyIce. SRT feeds from remote encoders (bonded cellular, satellite) arrive on separate mounts. TinyIce generates the HLS multivariant playlist for adaptive bitrate. WebRTC playback for VIP viewers needing minimal latency. All from one binary that starts in seconds—not the 20-minute Docker Compose dance.
Corporate Podcast & Video Platform
Internal communications need security and simplicity. TinyIce's OIDC integration with existing corporate identity providers. Passkeys eliminate password reset tickets. The embeddable player drops into intranet pages. AutoDJ plays recorded training content between live all-hands broadcasts. Prometheus metrics feed existing monitoring infrastructure.
Community Media & Low-Budget Broadcasters
Universities, community radio, churches—organizations with technical ambition but minimal budget. TinyIce runs on a $5 ARM VPS or Raspberry Pi. No license fees. No per-stream pricing. The Docker image deploys anywhere. WebRTC ingest means guests broadcast from browsers without installing OBS. The built-in AutoDJ fills dead air automatically.
Step-by-Step Installation & Setup Guide
Method 1: Pre-built Binary (Fastest)
# Download latest release for your platform
curl -LJO "https://github.com/DatanoiseTV/tinyice/releases/latest/download/tinyice-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
chmod +x tinyice-*
mv tinyice-* tinyice
# Run once to generate secure credentials
./tinyice
On first run, TinyIce generates cryptographically secure passwords and prints them:
FIRST RUN: SECURE CREDENTIALS GENERATED
Admin Password: Oy9…
Default Source Password: Rm3…
Live Mount Password: 8fX…
Save these immediately. Open http://localhost:8000 and log into the admin interface.
Method 2: Docker (Production Recommended)
# Run latest beta with persistent data volume
docker run --rm -p 8000:8000 -v tinyice-data:/data ghcr.io/datanoisetv/tinyice:beta
# Pin to specific release for reproducibility
docker run -d --name tinyice \
-p 8000:8000 -p 1935:1935 -p 9000:9000 \
-v $(pwd)/tinyice-data:/data \
-v $(pwd)/tinyice.json:/data/tinyice.json \
ghcr.io/datanoisetv/tinyice:v2.1.0
All images are multi-arch (linux/amd64, linux/arm64). Tags follow semantic versioning: :beta (newest beta), :latest (newest stable), :v2.1.0, :2.1, :2.
Method 3: Build From Source
# Requirements: Go 1.25+, Node.js 20+
git clone https://github.com/DatanoiseTV/tinyice.git
cd tinyice
make build # Builds frontend + embeds into Go binary
The make build process compiles the Preact frontend via Vite, then embeds all assets into the Go binary using go:embed. Result: one self-contained executable.
Configuration Essentials
Create tinyice.json in your working directory:
{
"bind_host": "0.0.0.0",
"port": "8000",
"base_url": "https://radio.example.com",
"page_title": "My Radio Station",
"page_subtitle": "Broadcasting 24/7 Worldwide",
"accent_color": "#ff6600",
"max_listeners": 1000,
"directory_listing": true,
"ingest": {
"rtmp_enabled": true,
"rtmp_port": "1935",
"srt_enabled": true,
"srt_port": "9000"
},
"trusted_proxies": ["127.0.0.1", "10.0.0.0/8", "172.16.0.0/12"]
}
For automatic HTTPS with Let's Encrypt:
{
"use_https": true,
"auto_https": true,
"port": "80",
"https_port": "443",
"domains": ["radio.example.com"],
"acme_email": "admin@example.com"
}
Bind to privileged ports without root:
sudo setcap 'cap_net_bind_service=+ep' ./tinyice
REAL Code Examples From the Repository
Example 1: FFmpeg Icecast Source Streaming
The simplest way to push existing audio files to TinyIce using FFmpeg's native Icecast protocol support:
# Stream MP3 file to TinyIce mount with proper content-type
ffmpeg -re -i input.mp3 -f mp3 -content_type audio/mpeg \
icecast://source:LIVE_MOUNT_PASSWORD@localhost:8000/live
What's happening here: The -re flag reads input at native playback rate—critical for streaming, not transcoding. -f mp3 forces MP3 muxer format. The icecast:// URL scheme is FFmpeg's native Icecast output; source is the standard Icecast username, followed by your mount's password, server address, and target mount point /live. TinyIce accepts this identically to classic Icecast2 servers.
Example 2: SRT Ingest with Stream ID Parameters
For professional broadcast workflows using SRT (Secure Reliable Transport):
# OBS, FFmpeg, or DVB mux output to SRT listener
srt://host:9000?streamid=#!::r=live,m=publish,key=LIVE_MOUNT_PASSWORD
Deep dive: SRT's streamid parameter carries TinyIce-specific metadata. The #!:: prefix identifies this as an access control block. r=live sets the target mount to /live. m=publish indicates this is a publisher (not subscriber) connection. key= authenticates against the mount's source password. This replaces complex srt-live-transmit pipelines—TinyIce's SRT listener demuxes MPEG-TS directly into its unified pipeline, delivering both audio and video tracks (a bug fix in v2.1.0 finally registered the video callback that was previously missing).
Example 3: OBS Simulcast Configuration (ABR Ladder)
Configure multiple quality tiers for adaptive bitrate streaming:
{
"variant_groups": {
"/live": ["/live", "/live_720", "/live_480"]
}
}
Then in OBS, configure three outputs:
| Output | Server | Stream Key |
|---|---|---|
| 1080p | rtmp://radio.example.com/live |
1080p_password |
| 720p | rtmp://radio.example.com/live_720 |
720p_password |
| 480p | rtmp://radio.example.com/live_480 |
480p_password |
The magic: TinyIce monitors each mount's live ingest metrics—actual measured bitrate and resolution. When a player requests https://radio.example.com/live/master.m3u8, it receives a multivariant playlist with accurate BANDWIDTH and RESOLUTION declarations for each variant. The built-in player auto-detects this and hands control to hls.js for adaptive switching. No manual bitrate estimation that becomes wrong when scene complexity changes. No external mediafilesegmenter tools. The BANDWIDTH values update dynamically based on real ingest data.
Example 4: Discord Webhook with Templated Notifications
Templated webhooks in v2.1.0 replace brittle external scripts:
{
"name": "Discord — #now-playing",
"url": "https://discord.com/api/webhooks/CHANNEL_ID/TOKEN",
"method": "POST",
"events": ["now_playing"],
"body_template": "{\"username\":\"TinyIce\",\"content\":\":musical_note: **{{.Mount}}** — {{.Artist}} – {{.Title}}{{if .MountURL}} — [Listen]({{.MountURL}}){{end}}\"}",
"enabled": true
}
Template engine deep-dive: TinyIce uses Go's text/template with custom helper functions. {{.Artist}} and {{.Title}} come from AutoDJ metadata. {{if .MountURL}} conditionally includes the listen link only when base_url is configured—graceful degradation. The urlencode helper (not shown here) handles special characters. For GET/HEAD methods, the rendered template appends as query string parameters, enabling TuneIn AIR Playing.ashx integration from a single template definition. Presets ship for Discord, Slack, Mattermost, Teams, Telegram, ntfy.sh, Pushover, and generic JSON.
Example 5: AutoDJ Shell Hook for External Integration
When HTTP webhooks aren't the right tool, use environment-variable hooks:
#!/bin/bash
# /opt/tinyice/track_notifier.sh
# Called by AutoDJ at each track start with full metadata context
curl -s "https://air.radiotime.com/Playing.ashx?\
partnerId=${PARTNER_ID}&\
partnerKey=${PARTNER_KEY}&\
id=${STATION_ID}&\
title=${TINYICE_TITLE}&\
artist=${TINYICE_ARTIST}"
Configure in TinyIce's AutoDJ settings with on_play_command: /opt/tinyice/track_notifier.sh. Available environment variables: TINYICE_ARTIST, TINYICE_TITLE, TINYICE_ALBUM, TINYICE_FILE, TINYICE_MOUNT. This runs asynchronously—no blocking the audio pipeline if the script is slow.
Advanced Usage & Best Practices
Zero-Downtime Configuration Reloads
Use ./tinyice reload or the admin UI to apply configuration changes without dropping active listeners. The hot-swap mechanism preserves existing connections while new configuration takes effect for new connections.
Trusted Proxies for Accurate Analytics
Always configure trusted_proxies when behind nginx, Caddy, Traefik, or Cloudflare Tunnel. Without this, all client IPs appear as 127.0.0.1, breaking rate limiting, ban functionality, and viewer counting. When trusted_proxies is non-empty, loopback stops being auto-whitelisted—security improvement and correctness fix.
OBS Encoder Optimization for TinyIce
Set 1 second keyframe interval in OBS for lowest HLS latency (matches default segment size). Use x264 with bf=0 or Profile baseline if using WHEP/WebRTC egress—B-frames break sub-second latency paths. For pure HLS, Main/High profile with standard GOP is fine.
Memory Tuning for High Listener Counts
Default burst buffer increased to 512 KiB in v2.1.0 for instant audio start without underruns. For 100,000+ listeners, monitor tinyice_memory_bytes in Prometheus and scale horizontally via relay federation rather than vertical scaling—TinyIce's single-binary design makes edge deployment trivial.
DVR Window for Live Seeking
Every video stream has 60 seconds of DVR by default—no configuration needed. Viewers can seek backwards during live broadcasts. Increase by modifying segment retention if your storage allows; the HLS playlist architecture supports arbitrary window sizes.
Comparison with Alternatives
| Capability | TinyIce | Icecast2 | nginx-rtmp | SRS | Restream |
|---|---|---|---|---|---|
| Binary Size | ~25 MB | ~2 MB + deps | nginx + module | ~50 MB | Cloud only |
| Audio + Video Unified | ✅ Native | ❌ Audio only | ⚠️ Video focus | ⚠️ Video focus | ✅ |
| Pure-Go / No FFmpeg | ✅ | ✅ | ❌ | ❌ | N/A |
| HLS A/V Output | ✅ Built-in | ❌ | ⚠️ Module | ✅ | ✅ |
| WebRTC Ingest | ✅ | ❌ | ❌ | ✅ | ✅ |
| WebRTC Playback (WHEP) | ✅ | ❌ | ❌ | ⚠️ Partial | ❌ |
| AutoDJ / Radio Automation | ✅ Native | ❌ | ❌ | ❌ | ❌ |
| Built-in Admin UI | ✅ SPA | ❌ Basic | ❌ | ⚠️ Basic | ✅ |
| OIDC / Passkey Auth | ✅ | ❌ | ❌ | ❌ | ✅ |
| Self-Hosted Cost | $0 | $0 | $0 | $0 | Per-hour |
| Operational Complexity | Low | Medium | High | Medium | None |
Why TinyIce wins: Icecast2 is rock-solid for audio but dead-ends at video. nginx-rtmp is unmaintained (last commit 2018) and requires manual HLS fragmenting. SRS is powerful but complex, with configuration that spans multiple files and concepts. TinyIce's unified model—one config file, one admin interface, one binary—reduces operational surface area dramatically while matching or exceeding feature coverage.
FAQ
Q: Can TinyIce replace my existing Icecast2 server without changing source software?
Absolutely. TinyIce speaks standard Icecast SOURCE/PUT protocol. BUTT, Mixxx, LadioCast, and FFmpeg icecast:// outputs work unchanged. The mount structure, password authentication, and metadata formats are compatible. Migrate by pointing existing sources at TinyIce's address.
Q: How does the pure-Go transcoder compare to FFmpeg quality?
For MP3 output, TinyIce uses the shine encoder with proper bitrate index management (fixed in v2.1.0). For Opus, it uses the reference libopus via cgo. The critical difference isn't quality—it's operational reliability. No forked processes, no zombie FFmpeg instances, no memory leaks from long-running transcodes. The automatic resampler eliminates common sample-rate mismatch bugs.
Q: What's the maximum listener count?
The project advertises "six figures of listeners"—100,000+ concurrent. This requires appropriate hardware (see PERFORMANCE.md in the repository) and bandwidth. For extreme scale, use TinyIce's relay federation or place edge instances behind a CDN with HLS caching.
Q: Is WebRTC playback production-ready?
WHEP egress is available with ?webrtc=1 parameter but marked opt-in while B-frame handling matures. HLS is the stable default. For sub-second latency today, ensure OBS outputs baseline profile (bf=0) and test thoroughly with your specific encoder configuration.
Q: How do I migrate from nginx-rtmp?
Replace your rtmp {} block with TinyIce's RTMP ingest (enabled in config). Convert application live {} concepts to TinyIce mounts. Replace hls_path and hls_fragment directives with TinyIce's automatic HLS generation. The OBS streaming settings remain identical—same URL format, same stream key concept.
Q: Can I use TinyIce without Docker?
The static binary is designed exactly for this. Download, chmod +x, run. No installation script, no dependency resolution, no apt-get install. The binary embeds all web assets, so the admin UI works immediately. Perfect for restricted environments where Docker isn't available.
Q: What about SSL/TLS?
TinyIce supports three modes: manual certificate files, automatic Let's Encrypt via ACME (set auto_https: true), or termination at your reverse proxy. The built-in ACME implementation handles renewal automatically; no certbot cron job needed.
Conclusion
TinyIce represents a fundamental shift in streaming infrastructure philosophy: complexity is the enemy of reliability. By collapsing ingest, transcoding, output, automation, and management into one battle-tested Go binary, it eliminates the integration failures that plague multi-service media stacks.
The pure-Go approach isn't just engineering purity—it's operational sanity. No more 3 AM pages because an FFmpeg process OOM'd. No more version conflicts between nginx and its RTMP module. No more explaining to non-technical staff why they need three different web interfaces to manage one broadcast.
Whether you're running a community radio station, streaming live events, or building the next podcast platform, TinyIce deserves evaluation. The migration path from existing Icecast infrastructure is painless. The feature set rivals commercial solutions costing thousands monthly. And the single-binary deployment model means you can test it locally in the time it takes to read this article.
Stop wrestling with your streaming stack. Grab TinyIce and broadcast like it's 2025.
→ Get TinyIce on GitHub — Star the repo, try the beta Docker image, or build from source. Your future self at 3 AM will thank you.