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.
{ config, pkgs, ... }:
{
imports = [ ./wallabag-mod.nix ];
# Wallabag reads its secret from $STATE_DIRECTORY/secret.txt.
# sops-nix will place the decrypted file there.
sops.secrets.wallabag_secret = {
owner = "wallabag";
path = "/var/lib/wallabag/secret.txt";
};
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
References
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
systemdmanaged/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 😊
{ 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 --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