Link rot is the slow failure of every bookmark folder. A page moves, the domain lapses, and the reference you saved now points at nothing. ArchiveBox fixes that by keeping its own copy of each URL on your disk, in many formats at once, so the copy survives even when the original page does not. On our test box the Wikipedia article on link rot became a 7.5 MB snapshot in 2 minutes 27 seconds, stored as HTML, PDF, a screenshot, MHTML, extracted text and raw HTTP responses across about 50 extractor folders.
To install ArchiveBox, this guide uses the official Docker Compose file, puts the server behind nginx on HTTPS, and switches off two defaults that publish more than most people expect. Those are a public snapshot index and automatic submission of every URL to the Internet Archive. The official ArchiveBox image (0.9.73, tested in October 2026 on Debian 13) pulled 4.32 GB and reported healthy 62 seconds after the first start, and all output below comes from it.
What ArchiveBox measured on the test box
Put plainly, the Docker image costs more disk than the first few hundred pages you archive, and a single snapshot takes one to two and a half minutes because a real Chromium session renders every page. The table holds the numbers from one Debian 13 VM with 7.76 GiB of RAM and a 40 GB disk.
| Measurement | Result on the test box |
|---|---|
Image pull (docker compose pull) | 1 min 36 s, 4.32 GB image |
First start to healthy (docker compose up -d --wait) | 1 min 2 s, including database migrations |
| Snapshot of example.com | 259.8 KB in 1 min 21 s |
| Snapshot of the Wikipedia “Link rot” article | 7.5 MB in 2 min 27 s |
data/archive after both snapshots | 12 MB |
| Idle container | 385.9 MiB RAM, 19 PIDs, about 1% CPU |
| Memory growth during one snapshot | 542 MB (runner log) |
| Cold backup of data, compose file and .env | 0.4 s, 3.6 MB tar.gz |
Two readings matter for planning. Per-page cost varied by a factor of about 30 between a bare test page and a media-heavy article, so disk sizing has to follow what you archive rather than how many URLs you add. Time per snapshot sits in minutes, not seconds, because the browser extractors on example.com each reported 48 to 52 seconds before the run finished. That makes ArchiveBox a background job for queues of links, and a poor fit for anyone who expects a one-click save to return instantly.
Each number came from the tool that produced it, with no manual stopwatch, so you can repeat any of them on your own server with the same commands.
- Timings: the shell
timewrapper around pull and start, and the “total time” line ArchiveBox prints for eacharchivebox add. - Sizes: the “total size” line from
archivebox add,du -shon the archive folder, anddocker imagesfor the image. - Idle footprint: one
docker stats --no-streamreading after both snapshots had sealed and all queues showed zero. - Memory growth: the admission line the runner printed during a snapshot, which reported 542431232 bytes of observed growth with 6386561024 bytes still available.
Prerequisites
ArchiveBox needs a Linux host with Docker, a DNS name for HTTPS and enough disk for both the image and the archive. The Compose file itself is distribution-agnostic, and only the Docker repository step below is written for Debian 13:
- Server: Debian 13 with sudo access. This is the only release tested here.
- RAM: 386 MiB idle plus about 540 MB per snapshot in progress. The runner admits up to 4 snapshots at once, so our estimate is about 4 GB for a full queue and 2 GB as the floor for one or two at a time. We did not measure four parallel snapshots.
- Disk: 4.3 GB for the image plus 0.25 to 7.5 MB per page in our two samples. The Compose file asks for the main data directory on a fast local SSD.
- Hostname: a DNS name pointing at the server. We used
archive.example.comwith a self-signed certificate. - Ports: 80 and 443 open for nginx. Port 5797 stays on loopback once Step 5 is done.
Treat those numbers as a floor for following along, not a production recommendation. A personal archive of a few thousand articles fits on a modest VM. A collection that pulls video through yt-dlp or mirrors whole sites grows by gigabytes per crawl, and the data directory then belongs on its own volume.
Step 1: Set the archive hostname
Your hostname appears in the ArchiveBox environment file, the TLS certificate, the nginx server block and every check against the proxy, so it goes into one variable. Setting it once means a typo can only happen in one place. Replace archive.example.com with the name your DNS record points at:
export SITE_DOMAIN="archive.example.com"
echo "Hostname: ${SITE_DOMAIN}"
Shell variables live only in the current session, and Step 2 asks you to log out for the docker group, which clears this one. Run the export line again when you log back in, and again at the start of Step 5 if you reconnected, otherwise the later commands write an empty hostname into the configuration.
Step 2: Install Docker CE and the Compose plugin
Debian 13 ships its own packages, docker.io 26.1.5 and docker-compose 2.26.1, but we installed Docker CE from the Docker repository so the Compose plugin matches what the ArchiveBox file is written for. ArchiveBox also installs with pip, but that route puts Chromium, Node and every extractor on the host itself. Docker is the route the official Compose file is built around and the only one tested here. Docker’s install documentation starts with ca-certificates and curl and a world-readable key file. Our lab VM already had both packages, so those two lines come from the docs rather than our log. Add the prerequisites, the Docker signing key and a deb822 source file.
sudo apt-get update && sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
printf "Types: deb\nURIs: https://download.docker.com/linux/debian\nSuites: trixie\nComponents: stable\nSigned-By: /etc/apt/keyrings/docker.asc\n" | sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null
sudo apt-get update
The Suites: trixie line is the codename of Debian 13; on Debian 12 it would be bookworm, which we did not test. Install the engine, the CLI and both plugins, then add your user to the docker group so the remaining commands run without sudo:
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker "$USER"
Membership of the docker group is equivalent to root on this host, because anyone in it can start a container that mounts the host filesystem, so add only the account that runs ArchiveBox. Group membership applies to new sessions only, so log out and back in before the next step. Then confirm both tools answer:
docker --version
docker compose version
Both commands must answer with a version string, because a missing plugin only shows up later as a confusing error from docker compose. The test box reported these.
Docker version 29.9.1, build dfc520d
Docker Compose version v5.6.0
With the plugin in place, every Compose command in this guide is written as docker compose with a space, and the standalone docker-compose binary that older guides downloaded by hand is not needed at all. Our Docker on Debian guide covers the repository setup in more detail.
Step 3: Install ArchiveBox with Docker Compose
ArchiveBox publishes its Compose file at a short URL, and that file is the supported way to run the current release. Older guides, including the previous version of this one, run the server on port 8000; the current file publishes port 5797 instead, so bookmarks and firewall rules from those guides will not match. Create the project folder and fetch the file:
mkdir -p ~/archivebox/data && cd ~/archivebox
curl -fsSL -o docker-compose.yml https://docker-compose.archivebox.io
At 131 lines, the file is mostly comments. With comments and blank lines stripped, this is everything it starts.
services:
archivebox:
image: ${ARCHIVEBOX_IMAGE:-archivebox/archivebox:latest}
restart: unless-stopped
ports:
- "${ARCHIVEBOX_PORT:-5797}:5797"
volumes:
- ./data:/data
- tmp:/tmp/archivebox
environment:
- PUID # optional numeric UID for NFS/CIFS/FUSE mounts (defaults to collection owner or 911)
- PGID # optional numeric GID for NFS/CIFS/FUSE mounts (defaults to collection group or 911)
- BASE_URL # canonical URL users will access this server at, can be set up via first-run wizard in the UI as well
- SERVER_SECURITY_MODE # defaults to "auto", can be set up via first-run wizard in the UI as well
- PUBLIC_ADD_VIEW=False # set to True to allow anonymous users to submit new URLs to archive
shm_size: "1gb" # chrome runs better when it can use a decently sized shm pool
pids_limit: 2048 # avoids low systemd task caps with Chrome and parallel extractors
volumes:
tmp:
Two details shape the rest of this guide. The image and the host port are both variables (ARCHIVEBOX_IMAGE and ARCHIVEBOX_PORT), so pinning a release and binding to loopback happen in a .env file instead of edits to the Compose file. There is no separate scheduler container anymore, because the server process now runs scheduled crawls itself. Pull the image first, since it is the slow part:
time docker compose pull
On the test box the pull took 1 minute 36 seconds and left a 4.32 GB image. Start the container and wait for its health check.
docker compose up -d --wait
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
The --wait flag returns only once the container health check passes, so the command itself tells you whether the start worked. On the test box that took 62 seconds, and the status column read healthy.
NAME STATUS PORTS
archivebox-archivebox-1 Up About a minute (healthy) 0.0.0.0:5797->5797/tcp, [::]:5797->5797/tcp
Most of that minute is the first-run initialisation: docker compose logs shows ArchiveBox verifying the folder structure, applying its Django migrations and then starting the worker_daphne, worker_sonic and worker_runner processes under supervisord. Note the 0.0.0.0:5797 binding. Until Step 5 moves it to loopback, the admin login is reachable over plain HTTP from your network, so do not leave a server in this state on a public address. Ask the container for its version:
docker compose exec archivebox archivebox version | head -3
The first three lines are the ones to read, because they confirm the release, the build date and that the process runs inside Docker. A long dependency table follows them.
0.9.73
ArchiveBox v0.9.73 COMMIT_HASH=6f5d2f6 BUILD_TIME=2026-10-06 20:11:16 1791317476
IN_DOCKER=True IN_QEMU=False ARCH=x86_64 OS=Linux
Further down, the same command lists the binary dependencies inside the image, including Chromium from Playwright, Node, wget, yt-dlp, git and the readability, mercury and defuddle parsers. None of them touch the host. On disk, ~/archivebox/data now holds ArchiveBox.conf, the SQLite index index.sqlite3, and the archive, logs, personas, sonic and sources folders. ArchiveBox.conf also carries a generated SECRET_KEY, so treat that file, and any backup of it, as a secret.
Step 4: Create the ArchiveBox admin user
A fresh collection has no users, and the root URL already redirects to a login form, so the admin account comes before anything else. This check uses the local port because nginx is not installed yet. Confirm the redirect against the local port:
curl -s -o /dev/null -w "GET http://127.0.0.1:5797/ -> %{http_code} %{redirect_url}\n" http://127.0.0.1:5797/
A 302 to /admin/login/ means the web server inside the container is answering. A refused connection means the container is still starting or failed its health check, so look at docker compose ps before going further.
GET http://127.0.0.1:5797/ -> 302 http://127.0.0.1:5797/admin/login/?next=/
Create the superuser non-interactively through the Django management command. Pick a real password in place of the placeholder, because this account can add, edit and delete everything in the archive:
docker compose exec -T -e DJANGO_SUPERUSER_PASSWORD='ChangeMe-Strong-Pass' archivebox archivebox manage createsuperuser --noinput --username admin --email [email protected]
The command is quiet when it works, so this single line is all you should see.
Superuser created successfully.
The password now sits in your shell history, so change it later with docker compose exec archivebox archivebox manage changepassword admin, which the login page itself also prints as the reset method. The Compose file offers ADMIN_USERNAME and ADMIN_PASSWORD environment variables as an alternative, but that keeps the password in a file next to the data, which we would avoid.
Step 5: Put ArchiveBox behind nginx on HTTPS
Inside the container ArchiveBox speaks plain HTTP, so nginx terminates TLS in front of it, and ArchiveBox itself needs three settings to behave behind a single hostname. None of them needs an edit to the Compose file. All three go into ~/archivebox/.env, which Compose reads automatically:
- BASE_URL: the public HTTPS address, which ArchiveBox uses when it builds links. Left empty, it printed links such as
http://admin.archivebox.localhost:5797/. - SERVER_SECURITY_MODE: the default
autodecides from the hostname, using subdomains only for*.localhostnames and one-domain mode without JavaScript replay for other hosts. We setsafe-onedomain-nojsreplayexplicitly, so the mode is pinned instead of relying on host detection. - ARCHIVEBOX_PORT: set to
127.0.0.1:5797so Docker publishes the port on loopback only and nginx becomes the single way in.
ArchiveBox source code lists five modes in total: auto, safe-subdomains-fullreplay, safe-onedomain-nojsreplay, unsafe-onedomain-noadmin and danger-onedomain-fullreplay. If you can give ArchiveBox a wildcard subdomain, the subdomain mode replays pages fully, but we only tested the one-domain mode, so that is the one configured here. Write the file and recreate the container:
cd ~/archivebox
printf "BASE_URL=https://%s\nSERVER_SECURITY_MODE=safe-onedomain-nojsreplay\nARCHIVEBOX_PORT=127.0.0.1:5797\n" "${SITE_DOMAIN}" > .env
cat .env
docker compose up -d --wait
Check the BASE_URL line first, because an empty variable from a new shell writes https:// with no hostname and ArchiveBox then builds broken links. Ours read as follows.
BASE_URL=https://archive.example.com
SERVER_SECURITY_MODE=safe-onedomain-nojsreplay
ARCHIVEBOX_PORT=127.0.0.1:5797
Recreating the container applies the new port binding, so the next check is whether the listener moved and whether ArchiveBox picked up the new values.
sudo ss -tlnp | grep 5797
docker compose exec archivebox archivebox config | grep -E "BASE_URL|SERVER_SECURITY_MODE"
Only 127.0.0.1:5797 should appear, owned by docker-proxy. A 0.0.0.0 line means Compose did not read the .env file, usually because the command ran outside ~/archivebox:
LISTEN 0 4096 127.0.0.1:5797 0.0.0.0:* users:(("docker-proxy",pid=96063,fd=8))
BASE_URL = "https://archive.example.com"
SERVER_SECURITY_MODE = "safe-onedomain-nojsreplay"
With the port on loopback, nothing on the network reaches ArchiveBox directly anymore, so the admin login is offline to everyone except this server until nginx sits in front of it later in this step.
Error: “[X] These options failed to get”
Our first attempt read these keys with archivebox config --get SERVER_SECURITY_MODE BASE_URL PUBLIC_INDEX, and every key came back under “[X] These options failed to get”, even though the full listing showed all of them. We did not chase the cause, since a working alternative was already at hand. Listing the whole configuration with archivebox config and filtering it with grep works every time, which is why the checks in this guide use that form.
Install nginx
nginx comes from the Debian repository, so no extra source is needed. Install it and check the version with sudo, for the reason the error below explains.
sudo apt-get install -y nginx
sudo nginx -v
Debian 13 installed this release from its main repository.
nginx version: nginx/1.26.3
That is the stock nginx from Debian 13, which supports the http2 on; directive used in the server block below, so no extra repository is needed.
Error: “nginx: command not found” right after the install
Run nginx -v as a regular user and the shell answers nginx: command not found with exit code 127, which looks like a failed install. It is not: the binary lives in /usr/sbin, and Debian does not put that directory on the PATH of a regular user, so sudo nginx -v or /usr/sbin/nginx -v finds it. Every nginx command in this guide already runs through sudo for that reason.
Create the TLS certificate
We used a self-signed certificate because archive.example.com was not a public name. Create one for your hostname, valid for 365 days and carrying the name in its subjectAltName field, which browsers require:
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout /etc/nginx/ssl/archive.key -out /etc/nginx/ssl/archive.crt -subj "/CN=${SITE_DOMAIN}" -addext "subjectAltName=DNS:${SITE_DOMAIN}"
sudo chmod 600 /etc/nginx/ssl/archive.key
For a hostname with a public A record and port 80 reachable, use a Let’s Encrypt certificate instead. After the server block below is in place, sudo apt-get install -y certbot python3-certbot-nginx followed by sudo certbot --nginx -d "${SITE_DOMAIN}" replaces the two certificate lines with the issued ones. We did not run certbot in this lab, so treat that path as the standard swap rather than a tested step. The nginx setup guide for Debian walks through it.
Write the nginx server block
Debian keeps one file per site in sites-available and enables it with a symlink, so the ArchiveBox server block gets a file of its own.
sudo vim /etc/nginx/sites-available/archivebox.conf
Paste the server block exactly as it ran on the test box. SITE_DOMAIN_HERE is a placeholder that the next command fills from your variable, so leave it as typed:
server {
listen 80;
server_name SITE_DOMAIN_HERE;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name SITE_DOMAIN_HERE;
ssl_certificate /etc/nginx/ssl/archive.crt;
ssl_certificate_key /etc/nginx/ssl/archive.key;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:5797;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
}
Three directives go beyond a plain proxy. The 100 MB body limit is there so the Upload button in the web UI can accept large bookmark exports. The Upgrade and Connection headers let WebSocket requests through in case the UI opens any, and the 300 second read timeout covers requests that wait on a slow crawl. The block listens on IPv4 only. Matching listen [::]:80 and listen [::]:443 ssl lines should cover IPv6, which we did not test. Substitute the hostname, enable the site, drop the Debian default site and test the configuration:
sudo sed -i "s/SITE_DOMAIN_HERE/${SITE_DOMAIN}/g" /etc/nginx/sites-available/archivebox.conf
sudo ln -sf /etc/nginx/sites-available/archivebox.conf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Never reload on a failed test, because nginx then keeps serving the old configuration and the mistake only surfaces later. Both lines must read “ok” and “successful”.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Test the redirect and both HTTPS paths from the server itself. The --resolve flag points the hostname at loopback and -k accepts the self-signed certificate; with a real DNS record and a publicly trusted certificate you can drop both:
curl -s -o /dev/null -w "http -> %{http_code} %{redirect_url}\n" -H "Host: ${SITE_DOMAIN}" http://127.0.0.1/
curl -sk --resolve "${SITE_DOMAIN}":443:127.0.0.1 -o /dev/null -w "https / -> %{http_code} %{redirect_url}\n" "https://${SITE_DOMAIN}/"
curl -sk --resolve "${SITE_DOMAIN}":443:127.0.0.1 -o /dev/null -w "https /admin/login/ -> %{http_code}\n" "https://${SITE_DOMAIN}/admin/login/"
Plain HTTP should answer 301 to the HTTPS address and the login page 200. Look closely at the middle line, because it is the first sign of the privacy problem in the next step: the root URL sends anonymous visitors to /public, not to the login form.
http -> 301 https://archive.example.com/
https / -> 302 https://archive.example.com/public
https /admin/login/ -> 200
Open https://archive.example.com/admin/ in a browser (with your own hostname) and sign in with the account from Step 4. A self-signed certificate triggers a browser warning that you accept once, and a certificate from a public CA does not.

Below the form, the login page prints the createsuperuser and changepassword commands for Docker Compose and native installs, which is the recovery path if you lose the admin password. Both run inside the container, so they work even when the web login is the thing that is broken.
Step 6: Turn off the public index and archive.org submission
By default, ArchiveBox lists every snapshot to anyone who can reach it, and it submits every URL you archive to the Wayback Machine at the Internet Archive. Both are defaults in the current release (PUBLIC_INDEX = true, PERMISSIONS = "public" and ARCHIVEDOTORG_ENABLED = true), and neither is mentioned during setup. For a personal research archive that is two leaks, because strangers can browse what you saved and a third party gets a copy of your reading list.
We found both the hard way, because the lab archived two pages before anyone looked at these settings. With two pages archived and no login, a request to /public on the test box returned 200 and both page titles. The archivedotorg/archive.org.txt file in the Wikipedia snapshot held a fresh Wayback URL, https://web.archive.org/web/20261020155640/https://en.wikipedia.org/wiki/Link_rot, created by ArchiveBox one minute into the crawl. This is the anonymous check we ran:
curl -skL --resolve "${SITE_DOMAIN}":443:127.0.0.1 -o /tmp/pub.html -w "anon /public (followed) -> %{http_code} %{url_effective}\n" "https://${SITE_DOMAIN}/public"
grep -oE "Link rot - Wikipedia|Example Domain" /tmp/pub.html | sort -u
This output is from our lab with two snapshots already archived. The anonymous request follows the redirect to /public/, and every archived title is in the page anyone can fetch; on an empty collection the same page simply shows an empty list.
anon /public (followed) -> 200 https://archive.example.com/public/
Example Domain
Link rot - Wikipedia
Do this step before you archive anything you would not post publicly, because a URL already submitted to archive.org stays submitted. Read the current values, then switch both settings off in one command:
cd ~/archivebox
docker compose exec archivebox archivebox config | grep -iE "ARCHIVEDOTORG|PERMISSIONS|PUBLIC_"
docker compose exec archivebox archivebox config --set PUBLIC_INDEX=False ARCHIVEDOTORG_ENABLED=False
In the screenshot you can see the defaults, the change, and what ArchiveBox wrote into data/ArchiveBox.conf. Read the note in the middle: turning off the public index also moved the default PERMISSIONS from public to unlisted, so new snapshots are served only to someone who already has the direct link.

Settings take effect after a restart. Restart the container, wait about 20 seconds for the server to come back as we did in the lab, and repeat the anonymous request:
docker compose restart
sleep 20
curl -skL --resolve "${SITE_DOMAIN}":443:127.0.0.1 -o /tmp/pub.html -w "anon /public after -> %{http_code} %{url_effective}\n" "https://${SITE_DOMAIN}/public"
grep -cE "Link rot - Wikipedia|Example Domain" /tmp/pub.html
Now the 200 is the login form at the end of a redirect: the effective URL must end in /admin/login/?next=/public/, and the count of titles must be zero. An anonymous visitor now gets a login prompt and nothing else, which is the state a private archive should be in.
anon /public after -> 200 https://archive.example.com/admin/login/?next=/public/
0
Confirm that the running server, not only the config file, carries the new values. Environment variables set in docker-compose.yml override ArchiveBox.conf, so if a future version of the Compose file ever sets ARCHIVEDOTORG_ENABLED=True, change it there as well:
docker compose exec archivebox archivebox config | grep -E "ARCHIVEDOTORG_ENABLED|PUBLIC_INDEX"
Both settings should now read false. Before the change this filter printed PUBLIC_INDEX = true and ARCHIVEDOTORG_ENABLED = true, as the screenshot above shows, and afterwards data/ArchiveBox.conf held ARCHIVEDOTORG_ENABLED = False and PUBLIC_INDEX = False. We did not capture this live listing after the restart, so the anonymous request above is our proof that the server applied them.
One gap remains. After the change, the Add form in the web UI still preselected “Public” in its Permissions menu, so a crawl started from the browser can override the new default. Pick Unlisted or Private on that form every time; the next step shows where it sits. PUBLIC_ADD_VIEW was already false, so anonymous users could not submit URLs at any point.
Step 7: Archive pages from the CLI and the web UI
Every way of adding URLs ends in the same queue, and the CLI is the one that prints timings. Run archivebox add inside the container from ~/archivebox:
time docker compose exec archivebox archivebox add "https://example.com"
ArchiveBox prints one line per extractor as it finishes, then a summary with the output folder. The first add ran before Step 5, which is why its admin link still uses the admin.archivebox.localhost form:
snapshot output saved to:
/data/archive/users/system/snapshots/20261020/example.com/06ad78e1049777798000b8f4cdec5785
crawl logs and setup:
./archive/users/system/crawls/20261020/example.com/06ad78e0dc877f5a80008d9bf1b39a63
http://admin.archivebox.localhost:5797/admin/crawls/crawl/06ad78e0dc877f5a80008d9bf1b39a63/change/
total urls snapshotted: 1
total size: 259.8 KB
total time: 1m 21s
Archiving the Wikipedia article took 2 minutes 27 seconds and 7.5 MB, and with BASE_URL set its crawl link pointed at https://archive.example.com/admin/crawls/.... That difference is the quickest way to tell whether the HTTPS settings are live, so look at it after every change to .env. Most of the size sits in a handful of extractors; these are the largest folders of that one snapshot, from du -sh:
| Extractor folder | Size | What it holds |
|---|---|---|
| responses | 2.8 MB | Raw HTTP responses captured by the browser |
| wget | 1.9 MB | wget mirror of the page and its assets |
| chrome_mhtml | 852 KB | Single-file MHTML from Chromium |
| archivewebpage | 612 KB | Capture from the ArchiveWeb.page extension |
| 512 KB | output.pdf (414 KB) plus a preview image | |
| screenshot | 508 KB | Full-page screenshot |
| singlefile | 364 KB | SingleFile HTML |
The raw responses and the wget mirror together make up more than half of this snapshot, so they are the first extractors to look at if disk growth worries you; we kept every extractor enabled for these numbers. List what the collection holds:
docker compose exec archivebox archivebox list | tail -5
Each snapshot comes back as one JSON line. The status of sealed means every extractor has finished, and the config field records the permission each snapshot was saved with, which is public for these two because they were archived before Step 6:
{"type": "Snapshot", "schema_version": "0.9.73", "id": "06ad78e1049777798000b8f4cdec5785", "crawl_id": "06ad78e0dc877f5a80008d9bf1b39a63", "url": "https://example.com", "title": "Example Domain", "tags": "", "bookmarked_at": "2026-10-20T15:51:44.286725+00:00", "created_at": "2026-10-20T15:51:44.286725+00:00", "modified_at": "2026-10-20T15:52:59.350792+00:00", "config": {"PERMISSIONS": "public"}, "notes": "", "timestamp": "1792511504.286725", "depth": 0, "status": "sealed", "fs_version": "0.9.4", "archive_size": 266073, "output_size": 266073}
Listed 2 snapshots
In the browser, the admin dashboard counts crawls, snapshots and the individual archive results; our single example.com snapshot had already produced 53 results. The status bar under the header shows the queue, which is the place to watch while a long crawl runs.

Clicking ADD opens the “Create a new Crawl” form. It takes URLs one per line or embedded in CSV, JSON, RSS or plain text, accepts an uploaded file, and applies tags, a persona and a permission level to the whole crawl. The Permissions menu at the bottom right is the one that still defaulted to Public after Step 6, so set it on every crawl you start from the browser.

Finished snapshots appear in the Snapshots view with their size and a row of icons for the main outputs. The 8 MB and 260 KB figures there match the CLI totals within rounding.

For recurring captures, the Compose file documents docker compose run --rm archivebox schedule --add --every=day --depth=1 'https://example.com/some/rss/feed.xml', which the running server picks up on its next due time. It also documents bulk import from a file with docker compose exec -T archivebox archivebox add < ~/Downloads/bookmarks.txt. We did not run either in this lab, so treat both as documented rather than tested. If you only need a readable copy of a single article rather than a full archive, saving web pages as PDF with percollate is far lighter, and Karakeep suits a bookmark manager that only needs light page archiving. The ArchiveBox project on GitHub documents the remaining options.
Back up and upgrade the archive
Everything ArchiveBox knows lives in three places under ~/archivebox, and a consistent backup copies all three with the container stopped, because the SQLite index runs in WAL mode and changes under a live copy. The archive holds:
- data/: the SQLite index,
ArchiveBox.confwith itsSECRET_KEYand privacy settings, and every snapshot folder. - docker-compose.yml: the service definition as downloaded.
- .env: the hostname, security mode, loopback binding and any image pin.
Stop, archive and start again. The tar runs under sudo because some files in data/ can end up owned by the container user rather than yours:
cd ~/archivebox
docker compose stop
BACKUP=~/archivebox-backup-$(date +%F).tar.gz
time sudo tar -czf "${BACKUP}" -C ~/archivebox data docker-compose.yml .env
docker compose start
tar -tzf "${BACKUP}" | grep -E "index.sqlite3$|ArchiveBox.conf$|\.env$"
On the test box the tar took 0.4 seconds and wrote 3.6 MB for two snapshots, so the downtime is the stop and start, not the copy. The listing must show all three critical files:
data/index.sqlite3
data/ArchiveBox.conf
.env
A backup is only proven by reading it back. In the same shell, so BACKUP still points at today’s file, we extracted it to a scratch folder and counted the snapshot rows with Python, since the sqlite3 CLI is not installed on a minimal Debian server:
mkdir -p /tmp/restore && tar -xzf "${BACKUP}" -C /tmp/restore
python3 -c "import sqlite3;print(sqlite3.connect('/tmp/restore/data/index.sqlite3').execute('select count(*) from core_snapshot').fetchone())"
A count of two matched the live collection, so the index inside the tarball is complete and readable. Repeat that check on any backup you intend to rely on.
(2,)
Because the tarball carries the SECRET_KEY, store it where you would store a password file. If you run several stacks like this one, Dockge gives them a web view, but it manages the Compose stack and not the data, so this backup still applies. Upgrading is a pull and a recreate, and the first start after a new image runs any pending migrations the same way the first install did. Take the backup above first on every upgrade, since migrations rewrite the index and the tarball is your way back:
cd ~/archivebox
docker compose pull
docker compose up -d --wait
docker compose exec archivebox archivebox version | head -1
Our run returned “Healthy” and the same version, because latest was already the newest release. If you would rather move between releases deliberately, pin the image in .env. Check that the tag exists before you rely on it, because ArchiveBox tags carry no leading v:
docker manifest inspect archivebox/archivebox:0.9.73 >/dev/null 2>&1 && echo "tag 0.9.73 exists" || echo "tag 0.9.73 missing"
docker manifest inspect archivebox/archivebox:v0.9.73 >/dev/null 2>&1 && echo "tag v0.9.73 exists" || echo "tag v0.9.73 missing"
Only the bare number resolved, so a pin written as v0.9.73 would fail at pull time.
tag 0.9.73 exists
tag v0.9.73 missing
Append the pin to .env, and the ${ARCHIVEBOX_IMAGE} variable in the Compose file picks it up on the next docker compose up -d --wait:
echo "ARCHIVEBOX_IMAGE=archivebox/archivebox:0.9.73" >> ~/archivebox/.env # pin: known-good release, newer tags at https://hub.docker.com/r/archivebox/archivebox/tags
We confirmed the tag but did not run the stack pinned, so after appending the line check that archivebox version still reports the release you chose. With a pin in place, an upgrade becomes three deliberate actions: take the backup, change the tag in .env, then pull and recreate. That is the routine we would run on any archive worth keeping.