create new tag
view all tags

Introduction to LCFG : Part 6b

The following examples assume you are using the virtual appliance described in IntroductoryTutorial.

This tutorial follows on from IntroductoryTutorialPart6a. In the following examples we will work on the task of managing the nginx web server. Although in this tutorial we're only configuring this for a single machine keep in mind that this could be something you need to do for a large collection of machines. Once you go beyond a small number of machines doing this manually will be inefficient, tedious and there's the potential to miss out some of them.

The examples below use the LCFG buildtools, documentation is available for the tools on the LCFG website.

Getting Started

As with the previous tutorial start by opening several terminal windows. To see the entire process of a change to an LCFG source profile being applied tail the server and client log files - /var/log/lcfg/server and /var/log/lcfg/client. You will also need root access in one terminal so you can modify the source profile, that can be acquired using sudo bash with the password being the same as the username.

All the files for this tutorial are provided in the lcfg-tutorial package, for Focal they are stored in the /usr/share/lcfg/tutorial/examples/focal/. To ensure you have the latest version run apt update and apt upgrade before starting.

Example 10 - Writing a Perl Component


Before starting work on writing the component the nginx packages need to be manually installed (as the root user) like this:

apt install nginx

Normally LCFG would take care of all this with the LCFG apt component and the apteryx package manager.


To keep the examples simple only a small part of the nginx configuration will be managed via LCFG. The aim here is to make it possible to change the port which it listens on for http requests and also have an alternate document root directory. This makes some sense as apache is already running in the virtual appliance which means port 80 is not available for nginx. As well as LCFG resources for those 2 pieces of information a few other resources will be required to specific the locations of important files.

Edit the nginx.def.cin in the lcfg-nginx project directory to append the following resources:

port %integer
port 80

documentroot      /var/www/html

serverroot        /etc/nginx

main_file         <%nginx.serverroot%>/nginx.conf

default_template  sites-default.tt
default_file      <%nginx.serverroot%>/sites-available/default

Note that the port resource has been specified as an integer. Also the main_file and default_file resources have default values which refer to the serverroot resource. This is good practice so that if you ever wanted to use a non-standard location for the configuration only one resource would require modification.

To simplify the process this schema is available in defaults/nginx-1.def, copy that file into place like:

cp /usr/share/lcfg/tutorial/examples/focal/defaults/nginx-1.def /var/lib/lcfg/conf/server/defaults/

Normally to get the schema installed on the machine where the LCFG server is running the defaults package (e.g. lcfg-nginx-defaults) would be installed. Unfortunately the LCFG server on our virtual appliance has not been configured to search for schema files in the standard location used for files in those packages. (Those who are especially keen could modify the defpath option in the /etc/lcfg/server.args file and then restart the mkxprof daemon).

For your real network, depending on your access permissions, this might mean you need to contact the administrator of the LCFG server to get the defaults package installed when you have created a new one or modified an existing one.

Once copied into place the LCFG server will note the creation of this schema file but as it is not yet included in any profiles no changes will be triggered.

Header File

Next a header file is required to actually load the component into your LCFG profile.

If it doesn't already exist, create a local/options directory and install the example nginx.h file:

mkdir -p /var/lib/lcfg/conf/server/include/local/options
cp /usr/share/lcfg/tutorial/examples/focal/include/options/nginx.h /var/lib/lcfg/conf/server/include/local/options

If you examine the contents of that header file you will see it is like:


!profile.components        mADD(nginx)
profile.version_nginx      1

/* Register the nginx.service with the multi-user.target */


/* The component must start after the daemon */

/* Install the server packages (and dependencies) */

!profile.pkgcppopts     mADD(LCFG_OPTIONS_NGINX)

/* Install the component package */
!profile.packages       mEXTRA(lcfg-nginx=*)

nginx.ng_service        nginx

nginx.port              80

nginx.documentroot      /var/www/html

nginx.serverroot        /etc/nginx
nginx.main_file         <%nginx.serverroot%>/nginx.conf
nginx.default_template  sites-default.tt
nginx.default_file      <%nginx.serverroot%>/sites-available/default



  • This loads version 1 of the nginx component schema into the profile.
  • The defaults for all the resources are specified in the header so that they are easily mutated. Remember that defaults set in the schema are only applied at the very end of processing a profile if the resource does not already have a value, they are NOT initial values.
  • This includes the standard configuration for systemd which would be done using the LCFG systemd component.
  • This includes the component package and the nginx packages (plus any dependencies).

Once copied into place the LCFG server will note the creation of this header file but as it is not yet included in any profiles no changes will be triggered.


As with previous examples the nginx component will use a template to translate the LCFG resources into the required configuration file. For this simple example only the default site file (/etc/nginx/sites-available/default) is being managed. In reality a number of other files (in particular /etc/nginx/nginx.conf) would also need to be managed.

Copy the site file into the templates directory for the project like this:

cp /etc/nginx/sites-available/default ~/lcfg-nginx/templates/sites-default.tt

  1. Add a header line which says "## Managed by LCFG, DO NOT EDIT!"
  2. Find the two listen lines in the server configuration block and replace the 80 with [% port %]
  3. Find the root line and replace /var/www/html with [% documentroot %]
  4. Finally it is necessary to escape some $ characters which would otherwise affect how the Perl Template Toolkit processes the template, use the following sed command:

sed -i -e 's/\$/\\$/g' ~/lcfg-nginx/templates/sites-default.tt

Note there is one slash in the match and two in the substitution part of that regexp.

It is useful to go through this process manually as it replicates the exact process you would need to follow when creating a new template for a configuration file. If you are short on time or it hasn't quite worked there is a file in the lcfg-tutorial package which you can copy into place like this:

cp /usr/share/lcfg/tutorial/examples/focal/sites-default.tt ~/lcfg-nginx/templates/

Component Code

As this component is also managing a service some code is required. This is a Perl module which will be found in lib/LCFG/Component/Nginx.pm.cin. The module file will contain lots of example subroutines which you can delete, only a Configure method is required.

The first section states that this is a module named LCFG::Component::Nginx which is a sub-class of the LCFG::Component module (part of the ngeneric framework). It also loads the LCFG::Template module which will be used to generate the configuration file, note that exports some useful variables, in particular we need $PERLTMPL to specify that the Perl Template Toolkit should be used.

package LCFG::Component::Nginx; # -*- perl -*-
use strict;
use warnings;

use v5.10;


use parent qw(LCFG::Component);

use LCFG::Template qw($PERLTMPL $MODIFIED);

Next comes the Configure method itself. It generates the configuration file from the template then, if the file is modified, it checks it is valid (using the CheckConfig method) and then restarts the service if it is running.

sub Configure {
    my ( $self, $res, @args ) = @_;

    my $template = $res->{default_template}{VALUE};
    my $configfile = $res->{default_file}{VALUE};

    my $status = LCFG::Template::Substitute( $template, $configfile, $PERLTMPL, $res );

    if ( ! defined $status ) {
        $self->Error( "failed to create config file (see logfile)");
    elsif ( $status == $MODIFIED ) {
        $self->LogMessage("configuration changed");

   if ( $self->CheckConfig($res) ) {
       $self->Service( $res->{ng_service}{VALUE}, 'try-restart' );
   } else {
       $self->Error('configuration is not valid');



The CheckConfig method is an additional method which is local to this module. It gets the path to the main nginx configuration file from the resources then passes it the nginx command along with the -t (test) option. If a zero exit status is returned then the method returns true, otherwise it returns false.

sub CheckConfig {
    my ( $self, $res ) = @_;

    my $main_file = $res->{main_file}{VALUE};

    my $rc = system 'nginx', '-t', '-c', $main_file;

    return ( $rc == 0 ? 1 : 0 );


Editting the component module to modify the code so it is as above is a useful process but somewhat time consuming. The finished example module is available in the lcfg-tutorial package and can be copied into place like this:

cp /usr/share/lcfg/tutorial/examples/focal/Nginx.pm.cin ~/lcfg-nginx/lib/LCFG/Component

Source Profile

Finally include the header into the LCFG profile for the VM:

This is available in source/example10, either copy that file to overwrite your lcfg-tutorial profile (in /var/lib/lcfg/conf/server/source/) like:

cp /usr/share/lcfg/tutorial/examples/focal/source/example10 /var/lib/lcfg/conf/server/source/lcfg-tutorial

Or directly edit the lcfg-tutorial source profile, remove any previous configuration and add the following:

/* Basic example profile */

#include <local/os/ubuntu/minimal_focal.h>
#include <lcfg/hw/virtualbox.h>
#include <lcfg/options/desktop.h>
#include <lcfg/options/lcfg-server.h>

#include <local/options/nginx.h>

!nginx.port           mSET(8080)

/* eof */

Once you have modified the profile check the server and client logs. Once the change has reached the client you should see that it has accepted the new profile.

Verify that it has worked using qxprof like:

qxprof nginx

You should see the list of resources with the value for the port resource set to 8080.

Enabling the Component

At this stage the new resources are available but nothing has actually happened. This is because the component package has not been installed and the component has not been started.

Make a new release and then build the package:

cd ~/lcfg-nginx
lcfg-reltool minor
lcfg-reltool deb

At this point you would normally submit the newly built packages into a central apt package repository using a tool like dput. This will vary between sites so consult with your local LCFG administrator as to how that is done.

For this example, select the path to the generated component package file and manually install it (as root) using dpkg:

dpkg -i /home/guest/lcfgbuild/lcfg-nginx-0.1.0/lcfg-nginx_0.1.0-1_all.deb

The component will then need to be enabled and started with systemd:

systemctl enable lcfg-nginx
systemctl start lcfg-nginx

Check the component log to ensure that the file changed and there were no errors:

less /var/log/lcfg/nginx

You can also diff the default site file to see what changed:

diff -u /etc/nginx/sites-available/default{~,}

This should show that the file gained a header line and the two listen statements were altered to use port 8080.

You should now be able to successfully start the nginx service itself:

systemctl start nginx

Finally start firefox and access http://localhost:8080/, you should see a page with the title "Welcome to nginx!".

The directory being served (i.e. the document root) is /var/www/html which is shared with Apache. For extra marks, try changing the document root for nginx to some other directory and see what happens...

-- squinney - 2021-04-23

Topic revision: r4 - 2021-04-26 - squinney
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2021 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback