Lark is a self-hosted web music player for personal high-resolution music libraries. The backend is Go + Echo v5 + Ent ORM with SQLite by default, and the frontend is React/Vite with an embedded production build served by the Go binary.
- Scan a local music directory from the Library page.
- Upload individual audio files from the Library page.
- Stream files with HTTP Range support so browser seeking works smoothly.
- Play browser-friendly formats directly: MP3, FLAC, WAV, M4A/AAC, OGG/Vorbis, OPUS.
- Keep rarer hi-res formats such as AIFF, APE, DSF, DFF, and DST in the library; when direct browser playback is unreliable, Lark can use the optional
ffmpegCLI to transcode the stream to MP3. - Read metadata and cover art with pure-Go
github.com/dhowden/tag; optionalffprobeimproves duration, sample-rate, bit-depth, and embedded lyric discovery.
- Browse all songs, artists, albums, and playlists.
- Open an artist, album, or playlist as its own detail page instead of being sent back to the full library.
- Click a song artist or album artist to open the artist page with every matching library track.
- Start an artist, album, or playlist with one click from the detail page.
- Favorite songs and albums.
- Create playlists and add songs to them.
- Open the current playback queue from the bottom player controls.
- Insert one selected song as the next track, or select multiple library songs and insert them as the next batch.
- Switch play mode from the player controls: sequence, shuffle, and repeat-one.
- Prefer embedded lyrics when they exist in the audio file.
- If embedded lyrics are missing, automatically match lyrics by song title and artist across configured online channels; no provider names, links, IDs, or manual paste fields are exposed in the UI.
- Fullscreen lyrics are opened by clicking the album/record cover in the bottom player.
- LRC parsing supports offset tags, multiple timestamps on one line, millisecond precision, and same-timestamp original/translation grouping so auto-scroll targets the correct playback line.
- Set a temporary sleep timer directly from the bottom player; it is session-only and does not persist as an app setting.
- Language: Simplified Chinese and English. The app name is shown as 百灵 in Chinese and Lark in English.
- Themes follow the supplied player schemes: five dark themes (Deep Space Noir, Amber Film, Neon Coral, Arctic Aurora, Carbon Volt) plus five light themes (Milk Porcelain, Oat Latte, Mint Soda, Sakura Washi, Dusk Amber).
- Theme selection lives in Settings to keep the main player clean.
- Each theme remaps the player colors, cover treatment, progress/volume styling, and motion language to match its scheme.
- Adaptive layout supports desktop, tablet, and mobile use, with a desktop sidebar, tablet icon rail, and mobile bottom navigation.
- Backend: Go, Echo v5, Ent ORM
- Database: SQLite by default via
github.com/lib-x/entsqlite; PostgreSQL and MySQL can be selected by environment variables - Frontend: React, TypeScript, Vite
- Audio metadata:
github.com/dhowden/tag - Optional media tools:
ffprobefor metadata andffmpegfor fallback stream transcoding - Frontend serving: built assets embedded into the Go server with
go:embed - Automation: GitHub Actions for CI, release binaries, and Docker image publishing
Lark intentionally keeps the default build CGO-free:
/api/songs/:id/stream?mode=rawserves the original file viahttp.ServeFile, preserving Range requests.- The frontend uses
mode=autoso browser-compatible formats stream directly. - Browser-incompatible formats can be transcoded on demand by the optional
ffmpegbinary. go-astiav/FFmpeg bindings are not part of the default build because they require CGO and systemlibav*development packages, which would complicate multi-platform binary releases.
- Go 1.25+ / 1.26 recommended
- Node.js 22+
- pnpm 10+
- Optional:
ffmpegandffprobe
cd backend
go test ./...
go run ./cmd/serverDefault server settings:
| Environment variable | Default | Description |
|---|---|---|
LARK_PORT |
8080 |
HTTP port |
LARK_DATA_DIR |
./data |
App data directory |
LARK_LIBRARY_DIR |
./data/music |
Music library scan/upload directory |
LARK_DB_TYPE |
sqlite |
Database type: sqlite / sqlite3, postgres / postgresql, or mysql / mariadb |
LARK_DB_DSN |
empty | Database connection string. Leave empty for SQLite to use ./data/lark.db; for SQLite this can be a file: DSN or a plain file path. Required for PostgreSQL/MySQL. |
LARK_FRONTEND_ORIGIN |
* |
CORS origin |
LARK_ADMIN_USERNAME |
empty | Create the first admin automatically when the database has no users |
LARK_ADMIN_PASSWORD |
empty | Password for LARK_ADMIN_USERNAME; must be set together with username |
LARK_ADMIN_NICKNAME |
empty | Optional nickname for the auto-created admin |
FFMPEG_BIN |
ffmpeg |
Optional transcoder binary |
FFPROBE_BIN |
ffprobe |
Optional metadata probe binary |
LARK_CACHE_BACKEND |
badger |
Cache backend: badger, redis, memory, or none. If unset and Redis env vars are present, Redis is selected automatically. |
LARK_CACHE_TTL_SECONDS |
120 |
TTL for cached library list/query responses |
LARK_CACHE_DIR |
./data/cache/badger |
Badger cache directory when using the built-in KV backend |
LARK_BADGER_CACHE_MB |
empty | Advanced override for the built-in Badger cache memory budget in MB. Leave empty to auto-size from physical memory. |
LARK_REDIS_URL |
empty | Optional Redis URL, e.g. redis://:password@redis:6379/0. Takes precedence over host/password/db settings. |
LARK_REDIS_ADDR |
empty | Redis address. Setting this env var enables Redis when LARK_CACHE_BACKEND is unset; if Redis is explicitly selected without an address, runtime falls back to localhost:6379. |
LARK_REDIS_PASSWORD |
empty | Redis password |
LARK_REDIS_DB |
empty | Redis database number; runtime fallback is 0 when Redis is selected |
LARK_REDIS_KEY_PREFIX |
empty | Prefix for Lark cache keys in Redis; runtime fallback is lark:cache: when Redis is selected |
Release builds inject lark/backend/pkg/version values with Go -ldflags; the Web settings page displays the running version, commit, and build time from /api/health.
cd frontend
pnpm install
pnpm devFor a production-style embedded server build:
cd frontend
pnpm build # writes embedded assets to ../backend/web/dist
cd ../backend
go run ./cmd/serverdocker compose up -dFor unattended first-run setup, pass the initial admin variables before the first start:
LARK_ADMIN_USERNAME=admin \
LARK_ADMIN_PASSWORD='change-me-now' \
LARK_ADMIN_NICKNAME='Lark Admin' \
docker compose up -dThe default compose file stores app data and uploaded music in the lark_data volume. If your runtime already exposes a music directory inside the container, set LARK_LIBRARY_DIR to that in-container path; otherwise leave it as /app/data/music and use uploads/scans within the app data volume. The published Docker image already includes ffmpeg/ffprobe; no extra compose environment is required for the default transcoding and metadata probe paths. Recursive scans skip the platform bookkeeping directory named .shared-center, then continue scanning sibling directories while keeping the configured library root unchanged.
LARK_LIBRARY_DIR=/lzcapp/run/mnt/home docker compose up -dSQLite is used by default. To place the SQLite database somewhere else, set LARK_DB_DSN:
LARK_DB_TYPE=sqlite \
LARK_DB_DSN='file:/app/data/lark.db?cache=shared&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(10000)&_pragma=cache_size(-10000)&_pragma=temp_store(FILE)&_pragma=mmap_size(0)' \
docker compose up -dFor SQLite, a plain path such as LARK_DB_DSN=/app/data/lark.db is also accepted; Lark will expand it to the tuned SQLite file: DSN above.
To use another database, set both LARK_DB_TYPE and LARK_DB_DSN:
LARK_DB_TYPE=postgres \
LARK_DB_DSN='postgres://lark:secret@postgres:5432/lark?sslmode=disable' \
docker compose up -d
LARK_DB_TYPE=mysql \
LARK_DB_DSN='lark:secret@tcp(mysql:3306)/lark?parseTime=true&charset=utf8mb4&loc=Local' \
docker compose up -dBy default Lark uses the built-in Badger KV cache under LARK_CACHE_DIR; no external service is required. Badger memory is auto-sized from physical memory, and LARK_BADGER_CACHE_MB is only an advanced override for constrained or unusually large deployments. Redis is only used when you explicitly configure Redis-related environment variables or set LARK_CACHE_BACKEND=redis.
Use an external Redis:
LARK_REDIS_URL='redis://:password@redis.example.com:6379/0' docker compose up -d
# or
LARK_REDIS_ADDR='redis.example.com:6379' \
LARK_REDIS_PASSWORD='password' \
LARK_REDIS_DB=0 \
docker compose up -dRun the optional Redis service bundled in docker-compose.yml:
LARK_REDIS_ADDR=redis:6379 docker compose --profile redis up -dIf no LARK_REDIS_* variable is set, compose starts only Lark and keeps using the built-in Badger KV cache.
Then open:
http://localhost:8080
.github/workflows/ci.yml— installs frontend dependencies, runs frontend lint/build, syncs embedded assets, verifies Go modules, runsgo testandgo vet, builds the backend server, and verifies the Docker image build..github/workflows/binary.yml— creates release draft assets for Linux, macOS, and Windows when av*tag is pushed; also supports manual artifact builds..github/workflows/docker.yml— publishes multi-architecture Docker images to GHCR and, when Docker Hub secrets are configured, Docker Hub.
Required secrets for Docker Hub publishing:
DOCKERHUB_USERNAMEDOCKERHUB_TOKEN
GHCR publishing uses the repository GITHUB_TOKEN.