Learn Aria2 - Scripting & Automation
Series/Learn Aria2/Episode 18
Episode 18 of 23

Learn Aria2 - Scripting & Automation

In this episode we'll turn aria2 into an automation engine: reading exit codes, calling aria2c from shell scripts and cron, batch downloads with loops and xargs, JSON-RPC integration from Python, Node, and Go, and event notification with --on-download-complete.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 17 you extracted maximum speed from aria2 — multi-connection with -x and -s, max-concurrent-downloads, even disk cache tuning. But speed only matters if downloads can run unattended, be repeated many times, and be integrated into a larger workflow. This episode 18 changes aria2 from a tool into an automation engine.

The difference isn't the options used, but how you treat it: as a program that returns exit codes, can be called from scripts and cron, is controlled from applications via RPC, and tells you when a download finishes. Let's build it all.

Exit Codes: The Language Between aria2 and Scripts

Every time aria2c finishes, it exits with an exit code — a number that's the only language scripts can reliably read. Unlike curl, which handles one request, aria2 handles many downloads at once, so its exit code reflects the status of the whole session, not individual files.

check-exit-code.sh
aria2c --dir=/srv/downloads https://cdn.example.com/update.iso
echo "aria2 keluar dengan kode: $?"
CodeMeaning
0All downloads succeeded
1Unknown error
2Timeout
3Resource not found
4Too many "resource not found" errors (per max-file-not-found)
5Download aborted because speed was too slow
6Network problem
9Not enough disk space
19Name resolution failed (DNS)
24HTTP authentication failed
28Unrecognized option or wrong argument
32Checksum verification failed

Tip

Remember one weakness of aria2's exit codes: an error on an already finished download isn't reported — the exit code only reflects the last error that occurred. If you need per-file certainty, verify outside aria2 (e.g. checksums) or read its logs. This detail will matter in episodes 19 and 21.

Calling aria2 from Shell Scripts

The first principle of automation: never ignore the exit code. If a download is a prerequisite for the next step — for example a database that must exist before an application is installed — the script must stop when the download fails, not continue with incomplete data.

guard-if.sh
if aria2c --dir=/srv/downloads --max-tries=3 \
  https://cdn.example.com/update.iso; then
  echo "Download selesai, lanjut instalasi"
  ./install.sh
else
  echo "Download gagal, hentikan pipeline" >&2
  exit 1
fi

The if aria2c ...; then pattern is what turns aria2 into a logic block: success or failure determines the next flow. In cron, the same behavior decides whether a job counts as successful or triggers an alert — cron reads the script's exit code as the success signal.

Batch Downloads: Loops and xargs

Automation rarely deals with a single URL — usually dozens or hundreds. Two patterns are most common: loops for per-URL control (exit codes checked one by one), and xargs for parallel processing. The -P 4 flag runs up to 4 aria2c processes at once:

while read -r url; do
  [ -z "$url" ] && continue
  if aria2c --dir=/srv/downloads "$url"; then
    echo "OK: $url"
  else
    echo "GAGAL: $url" >&2
  fi
done < urls.txt

Be careful with this combination: each aria2c process can open many connections per file (from episodes 4 and 17). Four processes with -x 16 means up to 64 connections to the same server — enough to make the server block you. For batches against one host, lower -x per process, or better, hand the queue to the RPC daemon (below) so connections are managed globally.

RPC Integration into Applications

To build a personal download manager, run aria2 as an RPC daemon (from episode 15) and send JSON-RPC requests to port 6800. The advantages: the daemon keeps running, queues and progress are managed by aria2, and the application just sends methods like aria2.addUri. The JSON-RPC payload looks like:

rpc-payload.json
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "aria2.addUri",
  "params": [
    "token:RAHASIA",
    ["https://cdn.example.com/dataset.zip"],
    { "dir": "/srv/downloads" }
  ]
}

Notice the RPC secret is sent as the first params element with the token: prefix. Every method follows the same pattern: start params with the token, then the method arguments. Now call the http://localhost:6800/jsonrpc endpoint from your favorite language — these three examples only need the standard library:

import json
import urllib.request
 
payload = {
    "jsonrpc": "2.0",
    "id": "1",
    "method": "aria2.addUri",
    "params": [
        "token:RAHASIA",
        ["https://cdn.example.com/dataset.zip"],
        {"dir": "/srv/downloads"}
    ]
}
 
req = urllib.request.Request(
    "http://localhost:6800/jsonrpc",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as resp:
    print(resp.read().decode())

All three examples do exactly the same thing: send aria2.addUri with the secret and download options. The response contains a GID — the download's unique identifier — which you can save to monitor progress via aria2.tellStatus(gid) or stop it with aria2.remove(gid). This is the core of a personal download manager: queue, pause, resume, and notifications can all be built in your own language.

Event Notification: --on-download-complete

Good automation doesn't wait — it listens. aria2 has an event hook: a script run automatically when a certain event happens. The most useful is --on-download-complete, called with three arguments: the GID, the number of files, and the path of the first file.

on-complete.sh
#!/usr/bin/env bash
GID="$1"
NUM_FILES="$2"
FILE_PATH="$3"
 
LOG_FILE="/var/log/aria2-hooks.log"
FINAL_DIR="/srv/downloads/selesai"
mkdir -p "$FINAL_DIR"
 
echo "[$(date +%F_%T)] Selesai: $GID - $FILE_PATH" >> "$LOG_FILE"
mv "$FILE_PATH" "$FINAL_DIR/"

Attach the hook to a download:

attach-hook.sh
aria2c --dir=/srv/downloads \
  --on-download-complete /opt/hooks/on-complete.sh \
  https://cdn.example.com/file.zip

This hook is the bridge between aria2 and the outside world: moving files, triggering checksum verification, calling an API, or sending notifications. For failed downloads there's --on-download-error, and for anything that stops — success or failure — there's --on-download-stop. In episode 21 we'll use these hooks to build a complete download workflow.

Tip

When using hooks in cron or a daemon, make sure the script is executable (chmod +x /opt/hooks/on-complete.sh) and use an absolute path in --on-download-complete. A hook that can't run only appears as an error in the log — the download is still counted as complete, so this trap is easy to miss.

Closing

Episode 18 turned aria2 into an automation engine: understanding exit codes as the language of session failures, calling aria2c from shell scripts with the if ...; then pattern, running batch downloads with loops and xargs -P, building your own download manager via JSON-RPC from Python, Node, and Go, and listening for events with --on-download-complete.

The key takeaway: scripts don't read screens — they read exit codes and events. Everything that looks like manual work can become a callable function running unattended.

In the next episode, episode 19, we flip the point of view: when that automation fails, how do you read the evidence? Troubleshooting and debugging — from --log and --log-level to network, TLS, and BitTorrent errors. See you then!