Deploying ZNC from source on Debian Jessie

So you want to deploy ZNC

Chat clients are a dime a dozen, and chat services are everywhere. So the fact is ZNC is not really a needed thing any longer unless you're wanting to run your own bouncer. I personally run my own bouncer as a means to keep my conversations away from shared services; largely because I'm a wearer of tinfoil hats. If you are wanting to run your own bouncer ZNC is a fantastic way to go. It can resume conversations and keep you online so you never miss a message. It's a small and fairly simple system but works amazingly well.

Deploying ZNC from source has a lot of advantages. Chief among these advantages is the fact we're able to leverage a newer version of ZNC without also coupling our server to a random PPA or some other oddball repository. To make this all go we have to run a couple commands which will create users, install packages, build the ZNC source, create an init file and start the services.


First thing to do is create the system user for ZNC

## Add users to the system
groupadd --system --gid 10000 znc  
useradd -c "ZNC system user" \  
        --home-dir /var/lib/znc \
        --gid 10000 \
        --groups znc \
        --create-home \
        --system \
        --shell /usr/sbin/nologin \
        --uid 100000 \
        znc

I also created an admin user which is used to administer the server. This is an optional step.

groupadd --gid 10001 admin  
useradd -c "Admin user" \  
        --gid 10001 \
        --groups znc,admin \
        --create-home \
        --shell /bin/bash \
        --uid 100001 \
        admin

If you did create the admin user, give the user a password.

# Create a user password for admin
passwd admin  

The next thing to do is install a few of the dependencies we'll need to to successfully run ZNC and to build the source.

## Install system deps
apt-get -y install build-essential \  
                   libssl-dev \
                   libperl-dev \
                   pkg-config \
                   libicu-dev \
                   pkg-config \
                   automake \
                   autoconf \
                   git \
                   iptables-persistent

Once these packages are in place, create a directory to store the bits we're going to build.

## Build ZNC and install
mkdir /opt/znc  

Now create the skeleton where the ZNC will store configs and various other things.

for i in configs moddata modules users; do  
  mkdir -p "/var/lib/znc/${i}"
done  

Retrieve the latest source tarball and store it in

wget http://znc.in/releases/znc-latest.tar.gz -O /var/cache/znc.tar.gz  

Now extract the source code into our holding directory.

tar -xf /var/cache/znc.tar.gz -C /opt/znc --strip-components=1  

Once the source code has been extracted we're going to configure the package, make it, and then use the checkinstall command to build us a Debian package and install the software. This will allow us to upgrade our source later, or uninstall it, with general ease.

pushd /opt/znc  
  ./configure
  make
  export $(grep 'PACKAGE_VERSION=' configure)
  checkinstall --install=yes \
               --fstrans=no \
               --default \
               --pkgversion=$PACKAGE_VERSION \
               --pkgname=znc-$PACKAGE_VERSION \
               make install
popd  

If you intend to connect to your ZNC server using an iPhone I recommend installing the palaver plugin for ZNC, this is an optional step. To install this module we'll retrieve the source code from git, make it, and copy the module over to our modules directory. Have a look at the README for more information on the module itself.

cd /opt  
git clone https://github.com/cocodelabs/znc-palaver znc-palaver  
pushd znc-palaver  
  make
  cp palaver.so /var/lib/znc/modules/
popd  

Now that ZNC is installed let's create a unit file for ZNC. The unit file will ensure ZNC is started and stopped with the host machine. Create a file located here /etc/systemd/system/znc.service containing the following contents.

[Unit]
Description=ZNC IRC Bouncer  
After=syslog.target  
After=network.target

[Service]
Type=simple  
User=znc  
Group=znc

ExecStart=/usr/local/bin/znc --datadir=/var/lib/znc --foreground

# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=300  
Restart=on-failure  
RestartSec=150

[Install]
WantedBy=multi-user.target  

Now reload the init system daemon to ensure the unit file is ready to go.

## Load the init unit file
systemctl daemon-reload  

Before we can start ZNC we have to generate a basic configuration file. To do this we're going to run the interactive command to make the config file. Once you've executed this command follow the on screen steps. If you do not know the answer to a specific question simply press enter and accept the defaults. You can always change this later through the ZNC UI or via an active IRC connection as the administrative user.

## Create basic znc config
sudo -H -u znc bash -c '/usr/local/bin/znc --makeconf --datadir=/var/lib/znc'  

Finalize the installation by ensuring all of the permissions are correct.

## Finalize the installation
chown -R znc. /var/lib/znc  
find /var/lib/znc -type d -exec chmod 2770 {} \;  
find /var/lib/znc -type f -exec chmod 2660 {} \;  

Now start the service.

## Start the service
systemctl start znc.service  

A couple of notes, I run my ZNC server on port 443 using an SSL certificate from LetsEncrypt. To do this I install certbot and run the following command to generate my certificate.

certbot certonly --standalone -d irc.example.com  # Change this domain name to match YOUR ACTUAL domain name.  

The previous command will retrieve the certificate from the LetsEncrypt API dropping the relevant files here: /etc/letsencrypt/live/irc.example.com. Once you have the certificate in place you will need to concatenate a few files and store the resultant in a location ZNC is able to read it. In this case I've stored the concatenated certificate in the ZNC home folder at /var/lib/znc/znc.pem.

# Change this domain name to match YOUR ACTUAL domain name you used above
cat /etc/letsencrypt/live/irc.example.com/{privkey,cert,chain}.pem > /var/lib/znc/znc.pem  

To get ZNC to respond to traffic on port 443 I use an IPTables rule to nat the traffic.

iptables -t nat -A PREROUTING -i ${NAME_OF_INTERFACE:-eth0} -p tcp -m tcp --dport 443 -j REDIRECT --to-ports ${DEFAULT_ZNC_PORT:-6667}  

Note the variables:

  • NAME_OF_INTERFACE: The interface name, I used eth0 by default.
  • DEFAULT_ZNC_PORT: Port number you setup when you generate the ZNC config. Have a look in /var/lib/znc/configs/znc.conf under the Listener listener0 section if you don't remember.

With all of this done you should now have ZNC installed from source running on your Debian system via a systemd unit file with palaver support all over SSL on port 443.

If you need, or would like, an example of a default config file, without users, this is an excerpt from the top my ZNC config file.

AnonIPLimit = 10  
ConnectDelay = 5  
HideVersion = true  
LoadModule = partyline  
LoadModule = webadmin  
LoadModule = palaver  
MaxBufferSize = 5000  
PidFile = /var/lib/znc/znc.pid  
ProtectWebSessions = true  
SSLCertFile = /var/lib/znc/znc.pem  
ServerThrottle = 30  
Skin = _default_  
StatusPrefix = *  
Version = 1.6.5

<Listener listener0>  
        AllowIRC = true
        AllowWeb = true
        IPv4 = true
        IPv6 = true
        Port = 6667
        SSL = true
        URIPrefix = /
</Listener>  

For a full breakdown on ZNC configuration options please refer to the upstream documentation.


Happy IRC'ing!