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 sudo privileges.
  • 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_PASSWORD to create the wallabag database and user with DB_PASSWORD, then creates the schema and a default user. That is why the MariaDB service only receives the root password.
  • SYMFONY__ENV__DOMAIN_NAME must 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_DSN is set to a null transport. Replace it with a real SMTP DSN such as smtp://user:[email protected]_provider.com:587 if 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:

  1. Log in with wallabag / wallabag.
  2. Open Config from the menu.
  3. On the Password tab, set a strong password.
  4. On the User information tab, set your name and email address.

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:

RuleTags
domainName = "github.com"code
readingTime >= 10long-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:

  1. Open API clients management from the menu.
  2. Click Create a new client, name it (for example "Firefox"), and leave the redirect URI empty.
  3. 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.