Wallabag is an open source read-it-later application: you send it the URL of an article, it extracts the readable content and stores it on your server so you can read it later on the web, in mobile apps or as an e-book. It is a self-hosted alternative to services like Pocket or Instapaper. In this tutorial you will deploy Wallabag with MariaDB and Redis using Docker Compose on Ubuntu 24.04, publish it over HTTPS with Nginx, secure the default account, and connect the browser extension and the API.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS with at least 1 GB of RAM, for example a CubePath VPS.
- A non-root user with
sudoprivileges. - Docker Engine and the Docker Compose plugin installed from Docker's official repository.
- A domain name with an A record pointing to your server. This guide uses
wallabag.your_domain; replace it with your own hostname. - Ports 80 and 443 reachable from the internet.
Step 1 - Creating the project directory and secrets
Create a directory for the Compose file, the database files and the images Wallabag downloads from saved articles:
sudo mkdir -p /opt/wallabag/db /opt/wallabag/images
cd /opt/wallabag
Generate the database passwords and a Symfony application secret, and store them in a .env file that Docker Compose reads automatically:
sudo tee /opt/wallabag/.env > /dev/null <<EOF
DB_ROOT_PASSWORD=$(openssl rand -hex 24)
DB_PASSWORD=$(openssl rand -hex 24)
APP_SECRET=$(openssl rand -hex 32)
EOF
sudo chmod 600 /opt/wallabag/.env
Step 2 - Writing the Docker Compose file
Create the Compose file:
sudo nano /opt/wallabag/docker-compose.yml
Paste the following content:
services:
wallabag:
image: wallabag/wallabag:latest
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
SYMFONY__ENV__DATABASE_DRIVER: pdo_mysql
SYMFONY__ENV__DATABASE_HOST: db
SYMFONY__ENV__DATABASE_PORT: "3306"
SYMFONY__ENV__DATABASE_NAME: wallabag
SYMFONY__ENV__DATABASE_USER: wallabag
SYMFONY__ENV__DATABASE_PASSWORD: ${DB_PASSWORD}
SYMFONY__ENV__DATABASE_CHARSET: utf8mb4
SYMFONY__ENV__SECRET: ${APP_SECRET}
SYMFONY__ENV__DOMAIN_NAME: https://wallabag.your_domain
SYMFONY__ENV__SERVER_NAME: "My wallabag"
SYMFONY__ENV__FOSUSER_REGISTRATION: "false"
SYMFONY__ENV__MAILER_DSN: "null://null"
SYMFONY__ENV__FROM_EMAIL: wallabag@your_domain
volumes:
- ./images:/var/www/wallabag/web/assets/images
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
db:
image: mariadb:11.4
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- ./db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 30s
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
How this setup works:
- On first start, the Wallabag container uses
MYSQL_ROOT_PASSWORDto create thewallabagdatabase and user withDB_PASSWORD, then creates the schema and a default user. That is why the MariaDB service only receives the root password. SYMFONY__ENV__DOMAIN_NAMEmust be the public HTTPS URL. Wallabag uses it to build links and asset URLs, and a wrong value causes broken styling.- Redis is used as the queue for imports.
SYMFONY__ENV__FOSUSER_REGISTRATION: "false"keeps public sign-up disabled; you will create accounts as an administrator.MAILER_DSNis set to a null transport. Replace it with a real SMTP DSN such assmtp://user:[email protected]_provider.com:587if you want Wallabag to send email (password resets, two-factor codes).- Port 8080 is published only on
127.0.0.1, because ports published by Docker bypass UFW.
Validate the file:
sudo docker compose config --quiet && echo "compose file OK"
compose file OK
Step 3 - Starting Wallabag
Start the stack:
sudo docker compose up -d
The first start takes one or two minutes while the database is initialized and the schema is created. Follow the progress and press Ctrl+C once the web server is running:
sudo docker compose logs -f wallabag
Then confirm that Wallabag answers locally. The /api/info endpoint returns the version without authentication:
curl -s http://127.0.0.1:8080/api/info; echo
{"appname":"wallabag","version":"2.6.13","allowed_registration":false}
Step 4 - Configuring Nginx and HTTPS
Install Nginx and Certbot:
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx
Allow web traffic through UFW:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
Create the server block:
sudo nano /etc/nginx/sites-available/wallabag
server {
listen 80;
listen [::]:80;
server_name wallabag.your_domain;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
The larger client_max_body_size lets you upload import files from other services. Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/wallabag /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Obtain a Let's Encrypt certificate; Certbot adds the TLS settings and the HTTP to HTTPS redirect for you:
sudo certbot --nginx -d wallabag.your_domain
Open https://wallabag.your_domain. You should see the Wallabag login page.
Step 5 - Securing the default account
The image creates an administrator account with the username wallabag and the password wallabag. Change it before doing anything else:
- Log in with
wallabag/wallabag. - Open Config from the menu.
- On the Password tab, set a strong password.
- On the User information tab, set your name and email address.
WarningAnyone who knows the default credentials can log in until you change the password, so do this right after the certificate is issued.
To add accounts for other people, open Users management from the menu (visible to administrators) and create them there.
Step 6 - Saving articles
To test the setup, click the + icon at the top of the article list, paste the URL of any article and press Enter. Wallabag downloads the page, extracts the main content and shows it in a clean reading view with an estimated reading time.
From the article view you can:
- Mark it as read (archive) or star it.
- Add tags with the tag icon.
- Select text and add an annotation, which is stored with the article.
Automatic tagging rules
Tagging rules apply tags to new articles automatically. Go to Config > Tagging rules and add rules such as:
| Rule | Tags |
|---|---|
domainName = "github.com" | code |
readingTime >= 10 | long-read |
title matches "kubernetes" | devops |
Rules are applied when an article is saved. Existing articles can be retagged from the same page.
RSS feeds
Wallabag can publish your unread, starred and archived lists as feeds. In Config > Feeds, generate a feed token; the page then shows the feed URLs for each list, which you can add to any RSS reader.
Step 7 - Connecting the browser extension and mobile apps
Browser extensions and apps authenticate through OAuth, so each one needs an API client:
- Open API clients management from the menu.
- Click Create a new client, name it (for example "Firefox"), and leave the redirect URI empty.
- Copy the Client ID and Client secret.
Install the Wallabagger extension, available for Firefox and Chromium-based browsers. In its options, enter https://wallabag.your_domain, check the URL, then fill in the client ID, client secret, your username and password, and click Get token. A green status means the extension can save pages with one click.
The official wallabag apps for Android (Google Play and F-Droid) and iOS ask for the same five values.
Step 8 - Using the REST API
The same client credentials work for scripts. Request an access token with the password grant (install jq with sudo apt install jq if needed):
TOKEN=$(curl -s -X POST https://wallabag.your_domain/oauth/v2/token \
-d grant_type=password \
-d client_id=your_client_id \
-d client_secret=your_client_secret \
-d username=your_username \
-d password=your_password | jq -r .access_token)
Save an article with tags:
curl -s -X POST https://wallabag.your_domain/api/entries.json \
-H "Authorization: Bearer $TOKEN" \
-d url=https://example.com/ \
-d tags=test,api | jq '{id, title, domain_name}'
{
"id": 3,
"title": "Example Domain",
"domain_name": "example.com"
}
Access tokens expire after one hour. Scripts should request a new one, or use the refresh_token returned with it.
Step 9 - Importing and exporting
Import (in the user menu) lists the services Wallabag can import from, including another wallabag instance, Instapaper, Pinboard and browser bookmark exports. Upload the export file from the old service and Wallabag fetches each article in the background through the Redis queue.
To export, open any list (unread, starred, a tag or a search) and use the export menu in the sidebar to download it in formats such as EPUB, PDF, JSON or CSV.
For a full backup of the server, dump the database and archive the images directory:
cd /opt/wallabag
sudo docker compose exec -T db sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" wallabag' | gzip > ~/wallabag-db-$(date +%F).sql.gz
sudo tar -czf ~/wallabag-images-$(date +%F).tar.gz -C /opt/wallabag images
Troubleshooting
The page loads without styles or redirects to http://. SYMFONY__ENV__DOMAIN_NAME does not match the public URL. Set it to https://wallabag.your_domain and run sudo docker compose up -d to recreate the container.
Some articles are saved empty or truncated. A few sites block server-side fetching or need a site-specific extraction rule. Use Re-fetch content from the article menu; if that fails, the browser extension can send the page content as rendered in your browser.
Wallabagger reports an authentication error. Check that the client ID and secret belong to the same API client and that the URL uses https://. Creating a new client in API clients management is often the quickest fix.
Conclusion
You have a private read-it-later service running on Ubuntu 24.04 with Docker Compose, protected by HTTPS, with the default password changed and the browser extension and API connected. As next steps, configure a real SMTP DSN so you can enable two-factor authentication, schedule the backup commands, and import your existing reading list from your previous service.
