Bonjour tout le monde,
Nouveau pc, nouvel OS oblige. Je me lance donc à la découverte de NixOS avec en tête la reproductibilité qu’il peut offrir.
J’ouvre ce sujet pour lancer la discussion, mais je ferai aussi un dépôt public prochainement pour partager ma configuration Nix
Bonne journée !
erics
Juillet 15, 2024, 1:28
2
@hop t’es toujours dans le coin ? voici un nouvel adepte de nix !!!
dev2a
Juillet 15, 2024, 1:33
3
J’ai un doute pour une utilisation journalière, tu risques de passer plus de temps à configurer ton env plutôt que de travailler .
J’aime bien le principe de l’isolation avec nix-shell au niveau projet, mais je n’irais pas plus loin avec nix
de toute façon, c’est un nouveau PC, donc ça se formate vite si c’est vraiment galère
Je me laisse la soirée pour avoir un fonctionnement qui me convient et avoir assimilé suffisamment de notions pour m’en sortir. Sinon on retournera aux bases : Debian testing.
dev2a
Juillet 15, 2024, 2:01
5
Linux Mint est bien, enfin surtout Cinnamon comme env de bureau, niveau productivité, il n’y a pas mieux, je trouve.
1 « J'aime »
dev2a
Juillet 15, 2024, 2:58
7
NixOs + i3wm + LazyVim
Tu es un extrémiste
Rassure-moi, ton navigateur, ce n’est pas lynx ?
erics
Juillet 15, 2024, 2:59
8
ha … parce-qu’il faut autre chose pour coder dolibarr ? w3m à la limite haute non ?
1 « J'aime »
LazyVim, c’est uniquement par économie de ressources, mais ça reste très très proche de codium, en moins lourd.
i3wm, ce sont des restes mais une fois que tu l’as en main, c’est comme Vim et associés : tu gagnes un temps de fou !
dev2a
Juillet 15, 2024, 3:20
10
Ça fait bien longtemps que pour le boulot, je ne regarde plus les ressources…
PhpStorm, datagrip, refact pour la complétion par IA. 64 Go de RAM, carte graphique de 16 Go et ça arrive de me sentir à l’étroit
C’est mon côté minimaliste, pour composer la place que je prends dans le monde non numérique
regis
Juillet 17, 2024, 1:15
12
@bfaliere il y a tellement de souffrance dans ce monde, pourquoi s’automutiler inutilement ?
Personne n’utilise Emacs comme éditeur ? Emacs 29.4
Le plaisir, voyons, quoi d’autre ?
knock
Avril 25, 2025, 7:51
15
Bonjour,
Je déterre le sujet, n’ayant pas trouvé grand chose d’autre qui parle de NixOS et Dolibarr sur ce forum.
Est-ce que le dépôt public avec ta configuration nixos existe quelque part ?
Bonjour,
Malheureusement j’ai dû interrompre mes activités plusieurs mois après ce post, et n’ai pas encore eu le temps de me repencher sur le sujet, mais il est toujours dans un coin de ma tête…
knock
Avril 25, 2025, 7:55
17
Ok, merci pour la réponse
1 « J'aime »
Après, si jamais tu as envie de te lancer sur le sujet, je peux ouvrir un dépôt, mais pour l’heure je n’ai rien à mettre dedans.
knock
Avril 25, 2025, 9:45
19
Je n’ai rien de vraiment spécifique pour Dolibarr dans ma config nixos, c’est une minuscule partie de ma config pour le dev php.
1 « J'aime »
knock
Juin 6, 2025, 7:09
20
Si ça intéresse quelqu’un voici ma config nix qui me permet de déployer plusieurs instances de dolibarr sur un serveur
{
lib,
config,
sources,
pkgs,
...
}:
let
inherit (config.networking) hostName;
inherit (config) bucketHash;
inherit (config) server-isProd;
app = "dolibarr";
cfg = config.app-dolibarr;
phps = import sources.nix-phps;
appDir = "/srv/www/${app}";
pwd = config.age.secrets."${app}.mysql.pwd".path;
dolPwd = config.age.secrets."${app}.dolibarr.pwd".path;
# On construit la liste des BDD nécessaires depuis la configuration du module
# et on s'assure que le user "app" a bien les droits sur ces BDD
ensureDatabases = builtins.catAttrs "database" cfg.erpList;
databasePrivileges = builtins.concatStringsSep "\n" (
builtins.map (
db: ''echo "GRANT ALL PRIVILEGES ON ${db}.* TO '${app}'@'localhost';"''
) ensureDatabases
);
# Configuration des virtuals hosts
vhost = erp: {
name = erp.domain;
value = {
forceSSL = true;
enableACME = true;
acmeRoot = null; # DNS-01 challenge
root = "${appDir}/${erp.company}/${erp.dolVersion}/htdocs";
extraConfig = ''
index index.php;
error_page 404 /index.php?controller=404;
'';
locations = {
"~ /\\." = {
extraConfig = ''
deny all;
'';
};
"~ \\.(log|tpl|twig|sass|yml)$" = {
extraConfig = ''
deny all;
'';
};
"~ ^(.+\\.php)(.*)$" = {
# Check that the PHP script exists before passing it
# tryFiles will reset path_info
# tryFiles = "$fastcgi_script_name =404";
# fix see https://trac.nginx.org/nginx/ticket/321
# set $path_info $fastcgi_path_info;
# fastcgi_param PATH_INFO $path_info;
# see https://github.com/Dolibarr/dolibarr/issues/6163#issuecomment-391265538
extraConfig = ''
# adresses ip autorisées à accéder à l'application
${pkgs.lib.ph-nginxAllowBlock cfg.allowedIps}
# socket
fastcgi_pass unix:${config.services.phpfpm.pools."dolibarr-${erp.company}".socket};
fastcgi_split_path_info ^(.+\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# Utilisé par tcpdf
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
## nécessaire pour modules custom dolibarr
fastcgi_param CONTEXT_DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Dolibarr Rest API path support
fastcgi_param PATH_INFO $fastcgi_path_info;
include ${config.services.nginx.package}/conf/fastcgi_params;
## TUNE buffers to avoid error ##
fastcgi_buffers 16 32k;
fastcgi_buffer_size 64k;
fastcgi_busy_buffers_size 64k;
'';
};
};
};
};
virtualHosts = builtins.listToAttrs (builtins.map vhost cfg.erpList);
# Configuration des pools phpfpm
pool = erp: {
name = "dolibarr-${erp.company}";
value = {
phpPackage = phps.packages.${builtins.currentSystem}.${erp.phpVersion}.buildEnv {
extensions = { enabled, all }: enabled ++ (with all; [ ]);
# recommandé par dolibarr
extraConfig = ''
memory_limit = 1G
session.use_strict_mode = 1
session.cookie_samesite = "Lax"
allow_url_fopen = 0
disable_functions = "pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals"
'';
};
user = "${app}";
settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
"listen.mode" = "0660";
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.max_requests" = 500;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 5;
"php_admin_value[error_log]" = "stderr";
"php_admin_flag[log_errors]" = true;
"catch_workers_output" = true;
};
phpOptions = ''
post_max_size = 10M
upload_max_filesize = 10M
'';
};
};
phpfpmPools = builtins.listToAttrs (builtins.map pool cfg.erpList);
configFile =
erp:
(pkgs.substituteAll {
src = ./conf.php;
dolibarr_main_url_root = "https://${erp.domain}";
dolibarr_main_document_root = "${appDir}/${erp.company}/${erp.dolVersion}/htdocs";
dolibarr_main_document_root_alt = "${appDir}/${erp.company}/${erp.dolVersion}/htdocs/custom";
dolibarr_main_data_root = "${appDir}/${erp.company}/documents";
dolibarr_main_db_file_path = "${dolPwd}";
dolibarr_main_db_name = "${erp.database}";
});
rule = erp: [
"d ${appDir}/${erp.company} 0750 ${app} ${app} - "
"d ${appDir}/${erp.company}/documents 0750 ${app} ${app} - "
"f ${appDir}/${erp.company}/documents/install.lock 0440 ${app} ${app} - "
"C ${appDir}/${erp.company}/${erp.dolVersion} 0750 ${app} ${app} - ${sources.${erp.dolVersion}}"
"C ${appDir}/${erp.company}/${erp.dolVersion}/htdocs/conf/conf.php 0400 ${app} ${app} - ${(configFile erp)}"
"L+ ${appDir}/${erp.company}/${erp.dolVersion}/htdocs/custom/multicompany - - - - ${appDir}/modules/multicompany"
];
tmpfilesRules = builtins.concatLists (builtins.map rule cfg.erpList);
backupPath = erp: "${appDir}/${erp.company}/documents";
resticBackupPath = (builtins.map backupPath cfg.erpList);
systemdDolibarrScripts = builtins.concatStringsSep "\n" (
builtins.map (erp: ''
${pkgs.util-linux}/bin/runuser -u ${app} -- \
${config.services.phpfpm.pools."dolibarr-${erp.company}".phpPackage}/bin/php -f \
${appDir}/${erp.company}/${erp.dolVersion}/scripts/cron/cron_run_jobs.php ${erp.cronSecurityKey} admin > ${appDir}/${erp.company}/documents/cron_run_jobs.php.log 2>&1
'') cfg.erpList
);
# Modules à installer
multicompany = builtins.path { path = ./modules/module_multicompany-18.0.7.zip;};
modules = [ multicompany ];
in
{
##
imports = [ ];
##
options = {
app-dolibarr = {
enable = lib.mkEnableOption "application dolibarr";
erpList = lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [ ];
description = "List of erp configurations.";
example = ''
[
{
company = "dolibarr";
database = "dolibarr_company";
domain = "dolibarr-company.example.com";
phpVersion = "php81";
dolVersion = "1805";
cronSecurityKey = "secret-key"
}
]
'';
};
sshKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "SSH keys to add to the user.";
};
# adresses ip autorisées à accéder à l'application
allowedIps = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
};
##
config = lib.mkIf cfg.enable {
# Configuration mot de passe user app
age.secrets = {
"${app}.mysql.pwd" = {
file = ../../../machines/${hostName}/secrets/${app}/${app}.mysql.pwd;
owner = "mysql";
};
"${app}.dolibarr.pwd" = {
file = ../../../machines/${hostName}/secrets/${app}/${app}.mysql.pwd;
owner = "${app}";
};
};
users = {
users.${app} = {
isNormalUser = true;
homeMode = "0750";
home = appDir;
group = "${app}";
extraGroups = [ "nginx" ];
openssh.authorizedKeys.keyFiles = builtins.map (file: ../../../keys + "/${file}") cfg.sshKeys;
};
groups.${app}.members = [
"${app}"
"restic"
];
users.nginx.extraGroups = [ "${app}" ];
};
# Configuration des modules
system.activationScripts.unzipModules = {
text = ''
for module in ${builtins.toString modules}; do
if [ -f $module ]; then
${pkgs.unzip}/bin/unzip -o $module -d ${appDir}/modules
chown --recursive ${app}: ${appDir}/modules
find ${appDir}/modules -type f -exec chmod 440 {} +
find ${appDir}/modules -type d -exec chmod 750 {} +
fi
done
'';
};
systemd = {
tmpfiles.rules = [
"d ${appDir}/modules 0750 ${app} ${app} - "
] ++ tmpfilesRules ++ [ ];
# timers systemd
services = {
dolibarr-taches-recurrentes = {
serviceConfig = {
Type = "oneshot";
};
script = '''' + systemdDolibarrScripts;
};
};
# Tous les timers sont configurés sur le fuseau horaire UTC
timers = lib.mkIf server-isProd {
dolibarr-taches-recurrentes = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*:0/15"; # toutes les 15 minutes
Persistent = true;
};
unitConfig = {
Description = "dolibarr tâches récurrentes";
Wants = "network.target";
After = "network.target";
};
};
};
};
# Création du user "app" pour accès à la base de données
# Alter user pour forcer le changement de mot de passe si le USER existe déjà
systemd.services.mysql.postStart = ''
( echo "CREATE USER IF NOT EXISTS '${app}'@'localhost' IDENTIFIED BY '$(cat ${pwd})';"
echo "ALTER USER '${app}'@'localhost' IDENTIFIED BY '$(cat ${pwd})';"
${databasePrivileges}
echo "FLUSH PRIVILEGES;"
) | ${config.services.mysql.package}/bin/mysql -N
'';
services = {
restic = {
backups = {
"${app}-files" = {
user = "restic";
repository = "rclone:ovh:restic.${hostName}.${bucketHash}/${app}";
initialize = true;
passwordFile = config.age.secrets."restic.pwd".path;
rcloneConfigFile = config.age.secrets."rclone.conf".path;
paths = [ ] ++ resticBackupPath;
pruneOpts = [ "--keep-last 7" ];
timerConfig = {
OnCalendar = "*-*-* 20:00:00";
Persistent = true;
RandomizedDelaySec = "15min";
};
};
};
};
mysql = {
inherit ensureDatabases;
};
phpfpm.pools = phpfpmPools;
nginx = {
enable = true;
virtualHosts = virtualHosts;
};
};
};
}
2 « J'aime »