Yoinker is a lightweight Go service that schedules and downloads files over HTTP/HTTPS at configurable intervals.
Is Yoinker right for you? Ask yourself:
- Do you need to fetch remote files on a schedule (interval or cron)?
- Would templated filenames or subdirectories save you manual work?
- Do you require duplicate suppression to avoid re‑processing identical data?
- Does your workflow benefit from post‑download hooks or pub/sub events?
- Are you looking for built‑in Prometheus metrics for observability?
- Do you host files behind a CDN or API that supports ETag/Last‑Modified caching?
- Would automatic unpacking (gzip, zip) and charset normalization streamline ingestion?
- Do you prefer a simple CLI for dev/test workflows alongside an HTTP API?
- CRUD API for managing download jobs
- Concurrent downloads with configurable limits
- Persistent storage using SQLite
- Automatic retries on failure
- Graceful shutdown and context cancellation
- Health and metrics endpoints
- Prometheus metrics endpoint (
/metrics) - Filename & subdirectory templating using Go
text/template - Duplicate download suppression (MD5-based)
- Post-download hooks and event emitters (shell commands or JSON)
- Cron and interval scheduling (
intervalorschedulevia cron syntax) - Templated URLs (e.g.
https://.../{{ .Now.Format "20060102" }}.csv) - Content-Disposition filename extraction
- Automatic unpacking of gzip/zip archives and charset transcoding to UTF-8
- HTTP caching (ETag/Last-Modified) and resumable downloads (HTTP Range)
- CLI tools:
yoinker add|ls|rm|stats|apply
- Go 1.24+
-
Clone the repository:
git clone https://github.com/Takenobou/yoinker.git cd yoinker -
Build the binary and run in server mode:
# Build the server (and CLI) export DB_PATH=./data/yoinker.db export HOOK_UNSAFE=1 # Enable post-download shell hooks export DOWNLOAD_ROOT=./downloads # before running, ensure DB_PATH is explicitly set (no default fallback) go build -o yoinker cmd/yoinker/main.go # Start the HTTP server ./yoinker
-
CLI usage examples:
# Add a job via CLI yoinker add --url=https://example.com/data.csv --interval=300 # List jobs yoinker ls # View download stats yoinker stats # Remove a job yoinker rm <jobID> # Apply jobs from a YAML file yoinker apply jobs.yaml
-
Or build a static binary:
CGO_ENABLED=0 GOOS=linux go build -a -o yoinker ./cmd/yoinker
-
Optionally use Docker:
docker build -t takenobou/yoinker . docker run -d \ -e DB_PATH=/data/yoinker.db \ -e DOWNLOAD_ROOT=/data/downloads \ -p 3000:3000 \ -v yoinker_data:/data \ takenobou/yoinker -
Or use Docker Compose (included):
docker-compose up -d
| Environment Variable | Description | Default |
|---|---|---|
DB_PATH |
SQLite file path or directory | required |
PORT |
HTTP server port | 3000 |
DOWNLOAD_ROOT |
Directory for saving downloads | downloads |
MAX_CONCURRENT_DOWNLOADS |
Maximum parallel downloads | 5 |
LOG_LEVEL |
Logging level (debug,info,error) |
info |
HOOK_UNSAFE |
Enable execution of post-download shell hooks | false |
If
DB_PATHpoints to a directory,yoinker.dbwill be appended automatically.
-
GET /jobsList all jobs -
GET /jobs/{id}Get details of a specific job -
POST /jobsCreate a new job{ "url": "https://example.com/file.txt", "interval": 60, "overwrite": false } -
PUT /jobs/{id}Update an existing job{ "url": "https://example.com/file.txt", "interval": 120, "overwrite": true, "enabled": true } -
DELETE /jobs/{id}Remove a job
-
GET /healthReturnsOKif service is up -
GET /health/detailsReturns JSON with uptime, database status, goroutines, memory stats