The Complete Computer

Wallabag

LifeTechEmacsArcology

wallabag is a self hostable application for saving web pages: Save and classify articles. Read them later. Freely.

Deploying Wallabag to The Wobserver

I tried to make the module configurable so that others could use it, but your mileage may vary. I have no intention of upstreaming this to nixpkgs because I don't really understand PHPFPM well enough to feel comfy being responsible for it, but it's good enough for the CCE .

nix source: :tangle ~/arroyo-nix/nixos/wallabag.nix
{ pkgs, ... }: { imports = [ ./wallabag-mod.nix ]; services.wallabag = { enable = true; domain = "bag.fontkeming.fail"; virtualHost.enable = true; parameters = { domain_name = "https://bag.fontkeming.fail"; server_name = "rrix's Back-log Black-hole"; locale = "en_US"; twofactor_sender = "wallabag@fontkeming.fail"; from_email = "wallabag@fontkeming.fail"; }; }; services.nginx.virtualHosts."bag.fontkeming.fail" = { useACMEHost = "fontkeming.fail"; addSSL = true; extraConfig = '' error_log /var/log/nginx/wallabag_error.log; access_log /var/log/nginx/wallabag_access.log; ''; }; }

Make sure to write your cookie secret to /var/lib/wallabag/secret.txt!

Another Attempt for a Wallabag NixOS module

The Wallabag module I was using was kind of fiddly and didn't work after I upgraded to 24.05, I stumbled across this one that has some improvements:

  • it runs a small patch on Wallabag's source to be able to keep the source directory immutable and storing ephemeral data in the systemd managed /var/lib, /var/cache, etc paths.

  • it loads some secrets from a config file on the file system rather than embedding the secret in to the nix store

  • i've gone and added options to the module so that parameters can be overwritten, some basic settings can be provided, etc. See below for usage example.

  • i stripped out some DRY utilities that the upstream author had created to manage all their PHPFPM applications. In theory i could port e.g. tt-rss and my other phpfpm applications to use these helpers, but i'd rather not 😊

nix source: :tangle ~/arroyo-nix/nixos/wallabag-mod.nix
{ config, lib, pkgs, ... }: let wallabag = pkgs.wallabag.overrideAttrs (attrs: { patches = builtins.filter (patch: builtins.baseNameOf patch != "wallabag-data.patch") attrs.patches ++ [ # Out of the box, Wallabag wants to write to various subdirectories of the project directory. # Let’s replace references to such paths with designated systemd locations # so that the project source can remain immutable. ../files/wallabag-data.patch ]; }); # Based on https://github.com/wallabag/wallabag/blob/2.6.6/app/config/parameters.yml.dist settings = { database_driver = "${cfg.database_type}"; database_host = null; database_port = 5432; database_name = "wallabag"; database_user = "wallabag"; database_password = null; database_path = null; database_table_prefix = "wallabag_"; database_socket = "/run/postgresql/.s.PGSQL.${toString config.services.postgresql.port}"; database_charset = "utf8"; domain_name = ""; server_name = "Wallabag"; # Needs an explicit command since Symfony version used by Wallabag does not yet support the `native` transport # and the `sendmail` transport does not respect `sendmail_path` configured in `php.ini`. mailer_dsn = "sendmail://default?command=/run/wrappers/bin/sendmail%%20-t%%20-i"; locale = "en"; # A secret key that's used to generate certain security-related tokens. "env(SECRET_FILE)" = "%env(string:STATE_DIRECTORY)%/secret.txt"; secret = "%env(file:resolve:SECRET_FILE)%"; # two factor stuff twofactor_auth = false; twofactor_sender = ""; # fosuser stuff fosuser_registration = false; fosuser_confirmation = false; # how long the access token should live in seconds for the API fos_oauth_server_access_token_lifetime = 3600; # how long the refresh token should life in seconds for the API fos_oauth_server_refresh_token_lifetime = 1209600; from_email = ""; # RabbitMQ processing redis_scheme = "unix"; redis_host = ""; # Ignored for unix scheme redis_port = 0; # Ignored for unix scheme redis_path = config.services.redis.servers.wallabag.unixSocket; redis_password = null; # Redis processing rabbitmq_host = ""; rabbitmq_port = 0; rabbitmq_user = ""; rabbitmq_password = ""; rabbitmq_prefetch_count = 0; # sentry logging sentry_dsn = null; } // cfg.parameters; php = cfg.php.package.withExtensions ({ enabled, all }: enabled ++ (with all; [ imagick tidy ])); commonServiceConfig = { CacheDirectory = "wallabag"; # Stores sessions. CacheDirectoryMode = "700"; ConfigurationDirectory = "wallabag"; LogsDirectory = "wallabag"; StateDirectory = "wallabag"; # Stores site-credentials-secret-key.txt. StateDirectoryMode = "700"; }; cfg = config.services.wallabag; in { options.services.wallabag = with lib; { enable = mkEnableOption (mdDoc "Wallabag read-it-later service"); package = mkOption { type = types.package; default = pkgs.wallabag; }; php.package = mkOption { type = types.package; default = pkgs.php; }; parameters = mkOption { type = types.attrsOf types.str; default = {}; description = mdDoc "Parameters to override from the default. See <https://doc.wallabag.org/en/admin/parameters.html> for values."; }; database_type = mkOption { type = types.enum [ "pdo_sqlite3" "pdo_pgsql" ]; default = if config.services.postgresql.enable then "pdo_pgsql" else "pdo_sqlite3"; defaultText = '' if config.services.postgresql.enable then "pdo_pgsql" else "pdo_sqlite3" ''; description = mdDoc '' The database engine name. Can be pdo_sqlite3 or pdo_pgsql. ''; }; domain = mkOption { type = types.str; description = "Bare domain name for Wallabag"; }; virtualHost.enable = mkEnableOption (mdDoc "Define nginx virtualhost for Wallabag"); }; config = lib.mkIf cfg.enable { environment.etc."wallabag/parameters.yml" = { source = pkgs.writeTextFile { name = "wallabag-config"; text = builtins.toJSON { parameters = settings; }; }; }; services.nginx = lib.mkIf cfg.virtualHost.enable { enable = true; virtualHosts = { "bag.fontkeming.fail" = { root = "${wallabag}/web"; extraConfig = '' add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; ''; locations."/" = { extraConfig = '' try_files $uri /app.php$is_args$args; ''; }; locations."/assets".root = "${wallabag}/app/web"; locations."~ ^/app\\.php(/|$)" = { extraConfig = '' fastcgi_pass unix:${config.services.phpfpm.pools.wallabag.socket}; include ${config.services.nginx.package}/conf/fastcgi.conf; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME ${wallabag}/web/$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT ${wallabag}/web; fastcgi_read_timeout 120; internal; ''; }; locations."~ /(?!app)\\.php$" = { extraConfig = '' return 404; ''; }; }; }; }; services.phpfpm.pools.wallabag = { user = config.users.users.wallabag.name; phpPackage = php; settings = { "catch_workers_output" = true; "listen.owner" = config.services.nginx.user; "listen.group" = "root"; "pm" = "dynamic"; "pm.max_children" = 5; "pm.start_servers" = 2; "pm.min_spare_servers" = 1; "pm.max_spare_servers" = 3; "php_admin_value[error_log]" = "/var/log/wallabag/error.log"; "php_admin_value[access_log]" = "/var/log/wallabag/access.log"; "php_admin_flag[log_errors]" = true; }; phpOptions = '' ; Set up $_ENV superglobal. ; http://php.net/request-order variables_order = "EGPCS" # Wallabag will crash on start-up. # https://github.com/wallabag/wallabag/issues/6042 # error_reporting = E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED memory_limit = 256M ''; settings = { # Accept settings from the systemd service. clear_env = false; }; }; users.users.wallabag = { isSystemUser = true; group = "wallabag"; }; users.groups.wallabag = {}; services.redis.servers.wallabag = { enable = true; user = "wallabag"; }; services.rabbitmq.enable = false; systemd.services.phpfpm-wallabag.serviceConfig = commonServiceConfig; systemd.services.wallabag-install = { description = "Wallabag install service"; wantedBy = [ "multi-user.target" ]; before = [ "phpfpm-wallabag.service" ]; after = [ "postgresql.service" ]; path = with pkgs; [ coreutils php phpPackages.composer ]; serviceConfig = { User = "wallabag"; Type = "oneshot"; } // commonServiceConfig; script = '' if [ ! -f "$STATE_DIRECTORY/installed" ]; then php ${wallabag}/bin/console --env=prod wallabag:install touch "$STATE_DIRECTORY/installed" else php ${wallabag}/bin/console --env=prod doctrine:migrations:migrate --no-interaction fi php ${wallabag}/bin/console --env=prod cache:clear ''; }; }; }
diff source: :tangle ~/arroyo-nix/files/wallabag-data.patch :comments none
diff --git a/app/AppKernel.php b/app/AppKernel.php index 61b734e06..0902c20fc 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -64,12 +64,12 @@ class AppKernel extends Kernel public function getCacheDir() { - return dirname(__DIR__) . '/var/cache/' . $this->getEnvironment(); + return getenv('CACHE_DIRECTORY') . '/' . $this->getEnvironment(); } public function getLogDir() { - return dirname(__DIR__) . '/var/logs'; + return getenv('LOGS_DIRECTORY'); } public function registerContainerConfiguration(LoaderInterface $loader) diff --git a/app/config/config.yml b/app/config/config.yml index 7f0a4ca6c..77b5175c8 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -1,5 +1,7 @@ imports: - - { resource: parameters.yml } + # Unfortunately, we cannot use %env(string:CONFIGURATION_DIRECTORY)%. Hardcoding the path for simplicity. + # https://symfony.com/doc/current/service_container/import.html#importing-configuration-with-imports + - { resource: '/etc/wallabag/parameters.yml' } - { resource: security.yml } - { resource: services.yml } - { resource: wallabag.yml } @@ -28,7 +30,7 @@ framework: session: # handler_id set to null will use default session handler from php.ini handler_id: session.handler.native_file - save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%" + save_path: "%env(string:CACHE_DIRECTORY)%/sessions/%kernel.environment%" cookie_secure: auto fragments: ~ http_method_override: true diff --git a/app/config/wallabag.yml b/app/config/wallabag.yml index bd57d6377..8e1cd0970 100644 --- a/app/config/wallabag.yml +++ b/app/config/wallabag.yml @@ -35,7 +35,7 @@ wallabag_core: fetching_error_message: | wallabag can't retrieve contents for this article. Please <a href="https://doc.wallabag.org/en/user/errors_during_fetching.html#how-can-i-help-to-fix-that">troubleshoot this issue</a>. api_limit_mass_actions: 10 - encryption_key_path: "%kernel.project_dir%/data/site-credentials-secret-key.txt" + encryption_key_path: "%env(string:STATE_DIRECTORY)%/site-credentials-secret-key.txt" default_internal_settings: - name: share_public