WordPress: Difference between revisions

From NixOS Wiki
imported>Onny
Set language package version to Wordpress package version
drop 24.05 compat
 
(51 intermediate revisions by 6 users not shown)
Line 1: Line 1:
[https://wordpress.org Wordpress] is a self-hosted content management web application, especially designed for blogging but also a good start to create your own website. It can customized with themes and an in-build site editor and further extended with plugins.
[https://wordpress.org WordPress] is a self-hosted content management web application, especially designed for blogging but also a good way to start creating your own website. It can be customized with themes and a built-in site editor and further extended with plugins.


== Installation ==
== Installation ==


{{Note|Parts of this instruction and module are not yet upstreamed and still being reviewed as an open PR.}}
A simple local setup of WordPress can be enabled with the following setup
 
A simple local setup of Wordpress can be enabled with the following setup


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost" = {
services.wordpress.sites."localhost" = {};
  enable = true;
  virtualHost.adminAddr = "hello@example.org";
};
</syntaxHighlight>
</syntaxHighlight>


Visit http://localhost to setup your new Wordpress instance. By default, a Mysql server is configured automatically so you won't have to setup the database backend.
Visit http://localhost to setup your new WordPress instance. By default, a Mysql server is configured automatically so you won't have to setup the database backend.


== Configuration ==
== Configuration ==


It is possible to configure the WordPress <code>wp-config.php</code> file declarative using the <code>settings</code> option. See examples below and [https://developer.wordpress.org/apis/wp-config-php upstream documentation] for available settings.
=== Language ===
=== Language ===


The default language of the Wordpress module will be English. It is possible to package additional languages and make them available for your specific instance. Using <code>extraConfig</code> you can configure the default language. In this example, we're going to package and enable the German language.
The default language of the WordPress module will be English. It is possible to enable additional language support for languages which are [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=wordpressPackages.languages already packaged]. Using <code>settings</code> you can configure the default language. In this example, we're going to enable the German language.
 
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost" = {
  languages = [ pkgs.wordpressPackages.languages.de_DE ];
  settings = {
    WPLANG = "de_DE";
  };
};
</syntaxHighlight>


Alternatively you can package your own language files following this example:
<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
{ pkgs, ... }: let
{ pkgs, ... }: let


   language-de = pkgs.stdenv.mkDerivation {
   wordpress-language-de = pkgs.stdenv.mkDerivation {
     name = "language-de";
     name = "wordpress-${pkgs.wordpress.version}-language-de";
     src = pkgs.fetchurl {
     src = pkgs.fetchurl {
       url = "https://de.wordpress.org/wordpress-${pkgs.wordpress.version}-de_DE.tar.gz";
       url = "https://de.wordpress.org/wordpress-${pkgs.wordpress.version}-de_DE.tar.gz";
       sha256 = "sha256-dlas0rXTSV4JAl8f/UyMbig57yURRYRhTMtJwF9g8h0=";
       hash = "sha256-dlas0rXTSV4JAl8f/UyMbig57yURRYRhTMtJwF9g8h0=";
     };
     };
     installPhase = "mkdir -p $out; cp -r ./wp-content/languages/* $out/";
     installPhase = "mkdir -p $out; cp -r ./wp-content/languages/* $out/";
Line 36: Line 43:
in {
in {


   services.wordpress.sites."localhost" = {
   services.wordpress.sites."localhost".languages = [ wordpress-language-de ];
    enable = true;
    languages = [ language-de ];
    virtualHost.adminAddr = "hello@example.org";
    extraConfig = ''
      define ('WPLANG', 'de_DE');
    '';
  };


}
}
</syntaxHighlight>
</syntaxHighlight>


Make sure that the source of your language package corresponds to the Wordpress package version you're using. Consult the [https://translate.wordpress.org translation portal] of Wordpress for the specific country and language codes available. This example is using the code <code>de_DE</code> (Germany/German) in the source URL and also the <code>extraConfig</code> part.
Consult the [https://translate.wordpress.org translation portal] of WordPress for the specific country and language codes available. This example is using the code <code>de_DE</code> (Germany/German) in the source URL and also the <code>settings</code> part.
 
=== Themes and plugins ===


=== Themes ===
Themes and plugins which are [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=wordpressPackages already packaged] can be integrated like this:
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost" = {
  themes = {
    inherit (pkgs.wordpressPackages.themes)
      twentytwentythree;
  };
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      antispam-bee
      opengraph;
  };
};
</syntaxHighlight>


Unfortunately themes and plugins [https://github.com/NixOS/nixpkgs/pull/173622 haven't been packaged yet in nixpkgs]. In case you want to package and add custom themes yourself, the configuration could look like this
Manually package a WordPress theme or plugin can be accomplished like this:


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
let
let


responsive = pkgs.stdenv.mkDerivation {
wordpress-theme-responsive = pkgs.stdenv.mkDerivation rec {
   name = "responsive";
   name = "responsive";
   src = pkgs.fetchurl {
  version = "4.7.9";
     url = http://wordpress.org/themes/download/responsive.1.9.7.6.zip;
   src = pkgs.fetchzip {
     sha256 = "06i26xlc5kdnx903b1gfvnysx49fb4kh4pixn89qii3a30fgd8r8";
     url = "https://downloads.wordpress.org/theme/responsive.${version}.zip";
     hash = "sha256-7K/pwD1KAuipeOAOLXd2wqOUEhwk+uNGIllVWzDHzp0=";
   };
   };
  buildInputs = [ pkgs.unzip ];
   installPhase = "mkdir -p $out; cp -R * $out/";
   installPhase = "mkdir -p $out; cp -R * $out/";
};
};
Line 70: Line 85:


   services.wordpress.sites."localhost" = {
   services.wordpress.sites."localhost" = {
     themes = [ responsive ];
     themes = {
     virtualHost.adminAddr = "hello@example.org";
      inherit wordpress-theme-responsive;
     };
   };
   };


Line 77: Line 93:
</syntaxHighlight>
</syntaxHighlight>


You can package any available Wordpress theme, for example from the [https://wordpress.org/themes official themes repository]. Be sure to replace the ''name'', url and ''sha256'' part according to your desired theme.
You can package any available WordPress extension, for example from the [https://wordpress.org/themes official theme] or [https://wordpress.org/plugins plugin repository]. Be sure to replace the ''name'', url and ''sha256'' part according to your desired extension.
 
If you want to automatically enable and activate the ''responsive'' theme, add this <code>settings</code> line
 
<syntaxHighlight lang="nix">
settings = {
  WP_DEFAULT_THEME = "responsive";
};
</syntaxHighlight>


If you want to automatically enable the ''responsive'' theme, add this </code>extraConfig</code> line
In case you want to automatically enable and activate the plugin, in this example ''akismet'', you can add following to <code>extraConfig</code>


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
extraConfig = ''
extraConfig = ''
   // Activate and enable theme responsive as default
   if ( !defined('ABSPATH') )
   define ('WP_DEFAULT_THEME', 'responsive');
    define('ABSPATH', dirname(__FILE__) . '/');
   require_once(ABSPATH . 'wp-settings.php');
  require_once ABSPATH . 'wp-admin/includes/plugin.php';
  activate_plugin( 'akismet/akismet.php' );
'';
'';
</syntaxHighlight>
</syntaxHighlight>


=== Plugins ===
=== Max upload filesize ===


Packaging plugins, for example from the [https://wordpress.org/plugins official plugins repository], is similar to the approach used above
The following example configuration changes the max upload filesize limit to 1GB for the WordPress instance with the hostname <code>localhost</code>. Change the [[phpfpm]] pool name according to your hostname.


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
let
services.phpfpm.pools."wordpress-localhost".phpOptions = ''
  upload_max_filesize=1G
  post_max_size=1G
'';
</syntaxHighlight>
 
=== Mail delivery ===
 
Mail clients like [[Msmtp]] can be used to configure mail delivery for WordPress. This can be useful for sending registration mails or notifications for new comments etc.
 
By default WordPress will use the sender mail <code>wordpress@example.org</code> where ''example.org'' is the primary domain name configured for the WordPress instance. By installing and using the plugin [https://wordpress.org/plugins/static-mail-sender-configurator/ static-mail-sender-configurator] it is possible to declaratively configure and change the sender address, for example to <code>noreply@example.org</code>.
 
<syntaxHighlight lang="nix">
services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      static-mail-sender-configurator;
  };
  extraConfig = ''
      // Enable the plugin
      if ( !defined('ABSPATH') )
        define('ABSPATH', dirname(__FILE__) . '/');
      require_once(ABSPATH . 'wp-settings.php');
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
      activate_plugin( 'static-mail-sender-configurator/static-mail-sender-configurator.php' );
  '';
  settings = {
    # Change sender mail address
    WP_MAIL_FROM = "noreply@localhost";
  };
};
</syntaxHighlight>
 
== Maintenance ==
 
=== Upgrading ===
 
WordPress automatically performs an database and software upgrade as soon as a new package version is installed. Major version upgrades of the <code>pkgs.wordpress</code> package are performed between every new NixOS release. In case you wish to switch to a newer major WordPress version while staying on your latest NixOS version, you can choose between WordPress package versions available in the repository.
 
For example switch to WordPress 6.4 instead of the the default WordPress package:
 
<syntaxHighlight lang="nix">
services.wordpress.sites."example.org" = {
  package = pkgs.wordpress6_4;
};
</syntaxHighlight>
 
=== Using wp-cli ===
 
wp-cli is a command line tool to configure and manage WordPress instances. The following example command creates a administration account with the name <code>test</code> and the password <code>test123</code>
 
<syntaxHighlight lang="bash">
sudo -u wordpress HOME=/var/lib/wordpress/example.org nix run nixpkgs#wp-cli -- --path=/nix/store/wxzcmjfkyk5nfk7vidzbz1mz28wnfl5b-wordpress-example.org-6.1.1/share/wordpress user create test test@example.org --role=administrator --user_pass=test123
</syntaxHighlight>
 
Change the home directory <code>/var/lib/wordpress/example.org</code> according to your instance domain name. The WordPress root directory is specified with <code>--path</code> and can be found in the generated web server configuration.
 
== Tips and tricks ==
 
=== Force https-URLs behind reverse proxy ===
 
In case you're running WordPress behind a reverse proxy which offers a SSL/https connection to the outside, you can force WordPress to use the https protocol
 
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost" = {
  settings = {
    # Needed to run behind reverse proxy
    FORCE_SSL_ADMIN = true;
  };
  extraConfig = ''
    $_SERVER['HTTPS']='on';
  '';
};
</syntaxHighlight>
 
=== Search engine optimization (SEO) ===
 
'''Meta information'''
 
The WordPress plugin [https://wordpress.org/plugins/wordpress-seo Yoast SEO] helps you to configure meta information of your WordPress page. You can install it like this


akismet = pkgs.stdenv.mkDerivation {
<syntaxHighlight lang="nix">
  name = "akismet";
services.wordpress.sites."example.org" = {
   src = pkgs.fetchurl {
   plugins = {
     url = https://downloads.wordpress.org/plugin/akismet.3.1.zip;
     inherit (pkgs.wordpressPackages.plugins)
    sha256 = "1i4k7qyzna08822ncaz5l00wwxkwcdg4j9h3z2g0ay23q640pclg";
      wordpress-seo;
   };
   };
  buildInputs = [ pkgs.unzip ];
  installPhase = "mkdir -p $out; cp -R * $out/";
};
};
</syntaxHighlight>
After enabling the plugin in the WordPress admin interface, finish the first-time installation wizard of Yoast SEO. In most cases the free features offered by the plugin should be sufficient, so you won't have to register or enable any premium extensions. Other integrations which are not needed can be disabled in ''Yoast SEO -> Integrations''.
It's worth tweaking settings in ''Yoast SEO -> Search Appearance''. Especially configuring a social image and organization logo including name and description is useful if your page gets shared and indexed.
SEO optimization can be performed page wise. In the page editor you'll find SEO analysis and tips on the right pane.
'''Picture compression'''


in {
Your website gets better ranking for search engines if it is optimized to load fast. The WordPress plugin [https://wordpress.org/plugins/webp-express webp-express] compresses your existing and future images automatically into a modern efficient image format and reduces their file size.


  services.wordpress.sites."localhost" = {
Following example installs the plugin and adds an additional writeable directory to the WordPress package, otherwise the plugin will fail due to permission issues. This hack only works for one specific instance, in this example for ''example.org''. Replace the site name on all occurrences.
    plugins = [ akismet ];
<syntaxHighlight lang="nix">
     virtualHost.adminAddr = "hello@example.org";
services.wordpress.sites."example.org" = {
  plugins = {
     inherit (pkgs.wordpressPackages.plugins)
      webp-express;
   };
   };
};
nixpkgs.overlays = [
  (self: super: {
    wordpress = super.wordpress.overrideAttrs (oldAttrs: rec {
      installPhase = oldAttrs.installPhase + ''
        ln -s /var/lib/wordpress/example.org/webp-express $out/share/wordpress/wp-content/webp-express
      '';
    });
  })
];
systemd.tmpfiles.rules = [
  "d '/var/lib/wordpress/example.org/webp-express' 0750 wordpress wwwrun - -"
];
</syntaxHighlight>
In the WordPress administrator interface go to ''Settings -> WebP Express''. One possible configuration which is suitable for the NixOS module could be
* Scope: Uploads only (we cannot convert theme files)
* Destination folder: Mingled (save webp converted images in the same place as original files)
* File extension: Set to ".webp"
* Destination structure: Image roots
* Disable all .htaccess rules (doesn't apply for any web server)
* Convert on upload: yes (future uploads will be converted to webp file format)
* Alter HTML: Replace image URLs (we'll only reference compressed webp images on the page)
* Reference webps that haven't been converted yet: Yes
* How to replace: The complete page
Further click on ''Bulk convert'' to convert all existing images.
'''Lazy load images'''
Using the WordPress plugin [https://wordpress.org/plugins/jetpack Jetpack], it is possible to enable lazy loading of images. That means, images only visible in the current view of the web browser are loaded. This will speed up initial page load.


}
<syntaxHighlight lang="nix">
services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      jetpack;
  };
};
</syntaxHighlight>
</syntaxHighlight>


In case you want to automatically enable the plugin, in this example ''akismet'', you can add following to <code>extraConfig</code>
After enabling the plugin, in the WordPress admin interface go to ''Jetpack -> Settings -> Performance'' and ensure that lazy loading of Images is enabled. Note that Jetpack comes with a lot of optional modules which should be disabled if not used. On the same page go to ''Debug'' in the bottom menu and click on the last link offering the list of all modules. Disable all modules you don't need instead of ''Lazy Images''. 
 
'''Webserver text compression'''
 
Compressing text served by the web server enhances page loading times. This example enables text compression on the webserver [[Nginx]]. Please refer upstream documentation in case you're going to use a different web server for your WordPress setup.


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
extraConfig = ''
services.nginx.extraConfig = ''
   // Activate and enable theme responsive as default
   gzip on;
   if ( !defined('ABSPATH') )
  gzip_vary on;
    define('ABSPATH', dirname(__FILE__) . '/');
   gzip_comp_level 4;
   require_once(ABSPATH . 'wp-settings.php');
  gzip_min_length 256;
   require_once ABSPATH . 'wp-admin/includes/plugin.php';
   gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
  activate_plugin( 'akismet/akismet.php' );
   gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
'';
'';
</syntaxHighlight>
</syntaxHighlight>
== Tips and tricks ==


=== Enable SSL behind reverse proxy ===
'''Minify and merge Javascript and CSS'''
 
Page load can further optimized by minify and merge Javascript and CSS files. The plugin [https://wordpress.org/plugins/merge-minify-refresh merge-minify-refresh] can be used to achieve this. The following example installs the plugin and creates an additional directory required for the plugin to work. This hack is specified for one specific instance for the domain ''example.org'' (replace all occurrences with your preferred domain name). Don't forget to enable the plugin in the WordPress admin interface.
 
<syntaxHighlight lang="nix">
services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      merge-minify-refresh;
  };
};
 
nixpkgs.overlays = [
  (self: super: {
    wordpress = super.wordpress.overrideAttrs (oldAttrs: rec {
      installPhase = oldAttrs.installPhase + ''
        ln -s /var/lib/wordpress/example.org/mmr $out/share/wordpress/wp-content/mmr
      '';
    });
  })
];
 
systemd.tmpfiles.rules = [
  "d '/var/lib/wordpress/example.org/mmr' 0750 wordpress wwwrun - -"
];
</syntaxHighlight>
 
'''Useful online tools'''
 
There are some resources which might be useful to check the SEO score of your page
 
* [https://pagespeed.web.dev Google PageSpeed Insights]
* [https://www.seobility.net/en/seocheck seobility SEO Checker]
 
=== Security hardening ===


In case you're running Wordpress behind a reverse proxy which offers a SSL/https connection to the outside, you can force Wordpress to use the https protocol
By enabling these two plugins, your WordPress login is protected by a simple numeric captcha and the xml-rpc api, used by alternative WordPress clients, gets disabled.


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost".extraConfig = ''
services.wordpress.sites."example.org" = {
  // Needed to run behind reverse proxy
 
  define('FORCE_SSL_ADMIN', true);
  plugins = {
  $_SERVER['HTTPS']='on';
    inherit (pkgs.wordpressPackages.plugins)
'';
      disable-xml-rpc
      simple-login-captcha;
  };
  extraConfig = ''
      // Enable the plugin
      if ( !defined('ABSPATH') )
        define('ABSPATH', dirname(__FILE__) . '/');
      require_once(ABSPATH . 'wp-settings.php');
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
      activate_plugin( 'disable-xml-rpc/disable-xml-rpc.php' );
      activate_plugin( 'simple-login-captcha/simple-login-captcha.php' );
  '';
};
</syntaxHighlight>
</syntaxHighlight>


Line 146: Line 350:
=== Enable logging ===
=== Enable logging ===


To enable logging add the following lines to <code>extraConfig</code>
To enable logging add the following lines to <code>settings</code> and <code>extraConfig</code>


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost".extraConfig = ''
services.wordpress.sites."localhost" = {
   define( 'WP_DEBUG', true );
   settings = {
  define( 'WP_DEBUG_LOG', true );
    WP_DEBUG = true;
   ini_set( 'error_log', '/var/lib/wordpress/localhost/debug.log' );
    WP_DEBUG_LOG = true;
'';
   };
  extraConfig = ''
    ini_set( 'error_log', '/var/lib/wordpress/localhost/debug.log' );
  '';
};
</syntaxHighlight>
</syntaxHighlight>


Line 161: Line 369:


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
services.wordpress.sites."localhost".extraConfig = ''
services.wordpress.sites."localhost" = {
  @ini_set( 'display_errors', 1 );
  extraConfig = ''
'';
    @ini_set( 'display_errors', 1 );
  '';
</syntaxHighlight>
</syntaxHighlight>


Please note that this exposes sensible information about your server setup therefore this option should not be enabled in production.
Please note that this exposes sensible information about your server setup therefore this option should not be enabled in production.


[[Category:Services]]
== Known issues ==
 
There are some known issues regarding the WordPress module on NixOS
 
* Some plugins assume an absolute persistent path which Nix doesn't provide https://github.com/NixOS/nixpkgs/issues/210895
* The wp-content root nor the plugin directories are writeable which prevents some plugins to work https://github.com/NixOS/nixpkgs/issues/150951
* The wordpressPackages set misses translation files for themes and plugins. This means only the English interface will be available https://github.com/helsinki-systems/wp4nix/issues/2
* Plugins and themes are managed by the NixOS module. Manually updating or installing them through the WordPress web interface is not supported at the moment
 
[[Category:Server]]
[[Category:Web Applications]]
[[Category:Web Applications]]

Latest revision as of 08:38, 3 December 2024

WordPress is a self-hosted content management web application, especially designed for blogging but also a good way to start creating your own website. It can be customized with themes and a built-in site editor and further extended with plugins.

Installation

A simple local setup of WordPress can be enabled with the following setup

services.wordpress.sites."localhost" = {};

Visit http://localhost to setup your new WordPress instance. By default, a Mysql server is configured automatically so you won't have to setup the database backend.

Configuration

It is possible to configure the WordPress wp-config.php file declarative using the settings option. See examples below and upstream documentation for available settings.

Language

The default language of the WordPress module will be English. It is possible to enable additional language support for languages which are already packaged. Using settings you can configure the default language. In this example, we're going to enable the German language.

services.wordpress.sites."localhost" = {
  languages = [ pkgs.wordpressPackages.languages.de_DE ];
  settings = {
    WPLANG = "de_DE";
  };
};

Alternatively you can package your own language files following this example:

{ pkgs, ... }: let

  wordpress-language-de = pkgs.stdenv.mkDerivation {
    name = "wordpress-${pkgs.wordpress.version}-language-de";
    src = pkgs.fetchurl {
      url = "https://de.wordpress.org/wordpress-${pkgs.wordpress.version}-de_DE.tar.gz";
      hash = "sha256-dlas0rXTSV4JAl8f/UyMbig57yURRYRhTMtJwF9g8h0=";
    };
    installPhase = "mkdir -p $out; cp -r ./wp-content/languages/* $out/";
  };

in {

  services.wordpress.sites."localhost".languages = [ wordpress-language-de ];

}

Consult the translation portal of WordPress for the specific country and language codes available. This example is using the code de_DE (Germany/German) in the source URL and also the settings part.

Themes and plugins

Themes and plugins which are already packaged can be integrated like this:

services.wordpress.sites."localhost" = {
  themes = {
    inherit (pkgs.wordpressPackages.themes)
      twentytwentythree;
  };
  plugins = {
    inherit (pkgs.wordpressPackages.plugins) 
      antispam-bee
      opengraph;
  };
};

Manually package a WordPress theme or plugin can be accomplished like this:

let

wordpress-theme-responsive = pkgs.stdenv.mkDerivation rec {
  name = "responsive";
  version = "4.7.9";
  src = pkgs.fetchzip {
    url = "https://downloads.wordpress.org/theme/responsive.${version}.zip";
    hash = "sha256-7K/pwD1KAuipeOAOLXd2wqOUEhwk+uNGIllVWzDHzp0=";
  };
  installPhase = "mkdir -p $out; cp -R * $out/";
};

in {

  services.wordpress.sites."localhost" = {
    themes = {
      inherit wordpress-theme-responsive;
    };
  };

}

You can package any available WordPress extension, for example from the official theme or plugin repository. Be sure to replace the name, url and sha256 part according to your desired extension.

If you want to automatically enable and activate the responsive theme, add this settings line

settings = {
  WP_DEFAULT_THEME = "responsive";
};

In case you want to automatically enable and activate the plugin, in this example akismet, you can add following to extraConfig

extraConfig = ''
  if ( !defined('ABSPATH') )
    define('ABSPATH', dirname(__FILE__) . '/');
  require_once(ABSPATH . 'wp-settings.php');
  require_once ABSPATH . 'wp-admin/includes/plugin.php';
  activate_plugin( 'akismet/akismet.php' );
'';

Max upload filesize

The following example configuration changes the max upload filesize limit to 1GB for the WordPress instance with the hostname localhost. Change the phpfpm pool name according to your hostname.

services.phpfpm.pools."wordpress-localhost".phpOptions = ''
  upload_max_filesize=1G
  post_max_size=1G
'';

Mail delivery

Mail clients like Msmtp can be used to configure mail delivery for WordPress. This can be useful for sending registration mails or notifications for new comments etc.

By default WordPress will use the sender mail wordpress@example.org where example.org is the primary domain name configured for the WordPress instance. By installing and using the plugin static-mail-sender-configurator it is possible to declaratively configure and change the sender address, for example to noreply@example.org.

services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      static-mail-sender-configurator;
  };
  extraConfig = ''
      // Enable the plugin 
      if ( !defined('ABSPATH') )
        define('ABSPATH', dirname(__FILE__) . '/');
      require_once(ABSPATH . 'wp-settings.php');
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
      activate_plugin( 'static-mail-sender-configurator/static-mail-sender-configurator.php' );
  '';
  settings = {
    # Change sender mail address
    WP_MAIL_FROM = "noreply@localhost";
  };
};

Maintenance

Upgrading

WordPress automatically performs an database and software upgrade as soon as a new package version is installed. Major version upgrades of the pkgs.wordpress package are performed between every new NixOS release. In case you wish to switch to a newer major WordPress version while staying on your latest NixOS version, you can choose between WordPress package versions available in the repository.

For example switch to WordPress 6.4 instead of the the default WordPress package:

services.wordpress.sites."example.org" = {
  package = pkgs.wordpress6_4;
};

Using wp-cli

wp-cli is a command line tool to configure and manage WordPress instances. The following example command creates a administration account with the name test and the password test123

sudo -u wordpress HOME=/var/lib/wordpress/example.org nix run nixpkgs#wp-cli -- --path=/nix/store/wxzcmjfkyk5nfk7vidzbz1mz28wnfl5b-wordpress-example.org-6.1.1/share/wordpress user create test test@example.org --role=administrator --user_pass=test123

Change the home directory /var/lib/wordpress/example.org according to your instance domain name. The WordPress root directory is specified with --path and can be found in the generated web server configuration.

Tips and tricks

Force https-URLs behind reverse proxy

In case you're running WordPress behind a reverse proxy which offers a SSL/https connection to the outside, you can force WordPress to use the https protocol

services.wordpress.sites."localhost" = {
  settings = {
    # Needed to run behind reverse proxy
    FORCE_SSL_ADMIN = true;
  };
  extraConfig = ''
    $_SERVER['HTTPS']='on';
  '';
};

Search engine optimization (SEO)

Meta information

The WordPress plugin Yoast SEO helps you to configure meta information of your WordPress page. You can install it like this

services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      wordpress-seo;
  };
};

After enabling the plugin in the WordPress admin interface, finish the first-time installation wizard of Yoast SEO. In most cases the free features offered by the plugin should be sufficient, so you won't have to register or enable any premium extensions. Other integrations which are not needed can be disabled in Yoast SEO -> Integrations.

It's worth tweaking settings in Yoast SEO -> Search Appearance. Especially configuring a social image and organization logo including name and description is useful if your page gets shared and indexed.

SEO optimization can be performed page wise. In the page editor you'll find SEO analysis and tips on the right pane.

Picture compression

Your website gets better ranking for search engines if it is optimized to load fast. The WordPress plugin webp-express compresses your existing and future images automatically into a modern efficient image format and reduces their file size.

Following example installs the plugin and adds an additional writeable directory to the WordPress package, otherwise the plugin will fail due to permission issues. This hack only works for one specific instance, in this example for example.org. Replace the site name on all occurrences.

services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      webp-express;
  };
};

nixpkgs.overlays = [
  (self: super: {
    wordpress = super.wordpress.overrideAttrs (oldAttrs: rec {
      installPhase = oldAttrs.installPhase + ''
        ln -s /var/lib/wordpress/example.org/webp-express $out/share/wordpress/wp-content/webp-express
      '';
    });
  })
];

systemd.tmpfiles.rules = [
  "d '/var/lib/wordpress/example.org/webp-express' 0750 wordpress wwwrun - -"
];

In the WordPress administrator interface go to Settings -> WebP Express. One possible configuration which is suitable for the NixOS module could be

  • Scope: Uploads only (we cannot convert theme files)
  • Destination folder: Mingled (save webp converted images in the same place as original files)
  • File extension: Set to ".webp"
  • Destination structure: Image roots
  • Disable all .htaccess rules (doesn't apply for any web server)
  • Convert on upload: yes (future uploads will be converted to webp file format)
  • Alter HTML: Replace image URLs (we'll only reference compressed webp images on the page)
  • Reference webps that haven't been converted yet: Yes
  • How to replace: The complete page

Further click on Bulk convert to convert all existing images.

Lazy load images

Using the WordPress plugin Jetpack, it is possible to enable lazy loading of images. That means, images only visible in the current view of the web browser are loaded. This will speed up initial page load.

services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      jetpack;
  };
};

After enabling the plugin, in the WordPress admin interface go to Jetpack -> Settings -> Performance and ensure that lazy loading of Images is enabled. Note that Jetpack comes with a lot of optional modules which should be disabled if not used. On the same page go to Debug in the bottom menu and click on the last link offering the list of all modules. Disable all modules you don't need instead of Lazy Images.

Webserver text compression

Compressing text served by the web server enhances page loading times. This example enables text compression on the webserver Nginx. Please refer upstream documentation in case you're going to use a different web server for your WordPress setup.

services.nginx.extraConfig = ''
  gzip on;
  gzip_vary on;
  gzip_comp_level 4;
  gzip_min_length 256;
  gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
  gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
'';

Minify and merge Javascript and CSS

Page load can further optimized by minify and merge Javascript and CSS files. The plugin merge-minify-refresh can be used to achieve this. The following example installs the plugin and creates an additional directory required for the plugin to work. This hack is specified for one specific instance for the domain example.org (replace all occurrences with your preferred domain name). Don't forget to enable the plugin in the WordPress admin interface.

services.wordpress.sites."example.org" = {
  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      merge-minify-refresh;
  };
};

nixpkgs.overlays = [
  (self: super: {
    wordpress = super.wordpress.overrideAttrs (oldAttrs: rec {
      installPhase = oldAttrs.installPhase + ''
        ln -s /var/lib/wordpress/example.org/mmr $out/share/wordpress/wp-content/mmr
      '';
    });
  })
];

systemd.tmpfiles.rules = [
  "d '/var/lib/wordpress/example.org/mmr' 0750 wordpress wwwrun - -"
];

Useful online tools

There are some resources which might be useful to check the SEO score of your page

Security hardening

By enabling these two plugins, your WordPress login is protected by a simple numeric captcha and the xml-rpc api, used by alternative WordPress clients, gets disabled.

services.wordpress.sites."example.org" = {

  plugins = {
    inherit (pkgs.wordpressPackages.plugins)
      disable-xml-rpc
      simple-login-captcha;
  };
  extraConfig = ''
      // Enable the plugin 
      if ( !defined('ABSPATH') )
        define('ABSPATH', dirname(__FILE__) . '/');
      require_once(ABSPATH . 'wp-settings.php');
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
      activate_plugin( 'disable-xml-rpc/disable-xml-rpc.php' );
      activate_plugin( 'simple-login-captcha/simple-login-captcha.php' );
  '';
};

Troubleshooting

Enable logging

To enable logging add the following lines to settings and extraConfig

services.wordpress.sites."localhost" = {
  settings = {
    WP_DEBUG = true;
    WP_DEBUG_LOG = true;
  };
  extraConfig = ''
    ini_set( 'error_log', '/var/lib/wordpress/localhost/debug.log' );
  '';
};

Since the default location to the folder wp-content is not writable, we redirect the log file path to /var/lib/wordpress/localhost/debug.log. All error messages will be stored there. Change the folder name localhost to the name of your site.

In case you want to print error messages directly in your browser, append following line

services.wordpress.sites."localhost" = {
  extraConfig = ''
    @ini_set( 'display_errors', 1 );
  '';

Please note that this exposes sensible information about your server setup therefore this option should not be enabled in production.

Known issues

There are some known issues regarding the WordPress module on NixOS