I am subscribed to a lot of blog feeds, but (sadly) I don’t have enough time to read all of them. This is especially the case for rather long (but often interesting) posts. Therefore it would be perfect to save these posts and read them in the near future.

There are already existing solutions for this like Mozilla Pocket. But as always I want to host this service on my own server (I have to justify the server somehow). A self-hosted solution is wallabag.

wallabag is a self-hostable PHP application allowing you to not miss any content anymore. Click, save and read it when you can. It extracts content so that you can read it when you have time. – https://github.com/wallabag/wallabag#what-is-wallabag

Wallabag needs 2 things to work properly:

  • A wallabag server to actually store the articles.

  • The wallabag browser plugin Wallabagger to add new articles to your backlog.

Additionally wallabag can be accessed via browser or one of the official apps (Android and iOS).

I am going to run the wallabag server as a container because it is easy to manage the configuration with git and to migrate it to a new host (or share it with you).

Installation with Docker

The following files are stored in this layout:

├── data
├── docker
│   ├── Dockerfile
│   └── nginx
│       ├── nginx.conf
│       └── wallabag
├── docker-compose.yml
└── images

The data and images directories will be used to store persistent data like images and the scraped articles.

The docker-compose.yml is based on an example provided by the wallabag project.

As I host the server at home this configuration is not hardened at all! I can’t recommend using this configuration to host this service on the public internet!

So let’s have a look at the main docker-compose file. We declare two containers, wallabag itself and a mariadb database. There is also a nginx container to handle missing icons, but more about this later.

version: '3'
          context: ./docker
          - wallabag
    image: wallabag/wallabag:2.4.1
      - MYSQL_ROOT_PASSWORD=wallaroot
      - SYMFONY__ENV__DATABASE_NAME=wallabag
      - SYMFONY__ENV__DATABASE_USER=wallabag
      - SYMFONY__ENV__FROM_EMAIL=wallabag@example.com
      - ./images:/var/www/wallabag/web/assets/images
    image: mariadb:10
      - MYSQL_ROOT_PASSWORD=wallaroot
      - ./data:/var/lib/mysql
    image: redis:alpine

The nginx is needed because of CORS errors. A short look on the GitHub issue tracker showed that this is a known problem. A fix is to use a nginx as reverse proxy and add the following header add_header 'Access-Control-Allow-Origin' 'https://example.com'. And this is exactly what I did.

The nginx is based on the upstream image, but we will run a quick apt update && apt upgrade to ensure we have the latest packages installed. Then we copy the base config nginx.conf and our site config wallabag into the container.

FROM nginx:1.19 AS nginx

RUN  export DEBIAN_FRONTEND=noninteractive \
    && apt-get update \
    && apt-get dist-upgrade -y --no-install-recommends

COPY nginx/wallabag /etc/nginx/sites-available/wallabag
COPY nginx/nginx.conf /etc/nginx/nginx.conf

RUN mkdir /etc/nginx/sites-enabled/ \
    && ln -s /etc/nginx/sites-available/wallabag /etc/nginx/sites-enabled/wallabag \
    && rm /etc/nginx/conf.d/default.conf


The main configuration of our nginx is basic, as I do not intent to expose this service to the public internet. I also do not care about error or access logs.

events {}

http {
    server_tokens off;
    sendfile on;
    keepalive_timeout 65;
    tcp_nodelay on;
    include /etc/nginx/mime.types;

    access_log off;
    error_log /dev/null;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

The site configuration exposes the server on port 1337 and adds the missing CORS header described earlier.

server {
        listen 1337;
        #server_name wallabag.foo.bar;

	location / {
		proxy_pass http://wallabag;
		proxy_set_header X-Forwarded-Host $server_name;
                proxy_set_header X-Forwarded-Proto https;
                proxy_set_header X-Forwarded-For $remote_addr;

        add_header 'Access-Control-Allow-Origin' 'http://DOMAIN'



Now to the easy part. Run docker-compose up to start the wallabag stack. Wallabag should now be online, and you can pair the browser plugin with the instance.