Old but robust : A Simple IRC Server

A preamble : why?

Old technology has its drawbacks and its advantages. Many early messaging protocols are very non-friendly to users by today’s standards.
The Internet Relay Chat (IRC) protocol, especially, does not let you upload pictures. It does not let you voice call.
It was designed in the late 80’s and early 90’s to replace older instant messaging protocols.
Of course, back then, mass distribution of multimedia content was impossible and, as such, it is not a core feature of IRC.
Furthermore, IRC is account-free, so a single device can connect as one or many users, and sessions end when the device disconnects, without data carrying over to other devices.
It is possible, however, to synchronize user information in order for the information of extended features (NickServ nickname registration, Operator identification, etc) to carry over server-side.

Nonetheless, IRC is somewhat robust.
It’s old, but to this day, there is still actively developed software on both server-side and client-side to use it, on just about any platform you could think of.
A server for a couple dozens of people does not require a lot of computing power, or memory.
In general, IRC is a light-weight protocol that is easy on bandwidth (by today’s standards at least).
The protocol’s standard also describes security systems like channel passwords, server passwords, and moderation operations.
Supplementary services (like those mentioned above) can also be deployed easily to add user-friendly features like channel registration, nickname protection, and server audit.

If I’m saying a lot of things you don’t get, I recommend you read one of the many tutorials online that cover the basic operations of connecting.
I will probably have to write one myself, since, well, IRC is your typical “made by tech folks for tech folks” kind of technology, and people who tend to use it already know what’s up.

One structural aspect of the protocol can be particularly appealing : servers that run IRC-compliant software can interconnect with each other with appropriate credentials and form a network.
Within the network, a user can connect to any trusted server, securely, over SSL (the ‘S’ in ‘HTTPS’), and messages, channels, private messages, are shared between servers among the network.
When one server goes down, provided that it is not the only server connecting two halves of the network, only the users connected to that server leave, but the remainder of the network, messages, channels, remain intact.
And, finally, servers can be hosted by anyone with a server that stays on all the time and is reachable from the internet (even from home, although it is not recommended for big, professional networks).

Recent societal events have reminded me just how important it could be to have decentralized, user-run services, to which one can communicate with basic encryption and without relying on servers outside of our control.

So I decided I would use the knowledge I have with IRC servers and tell you how to install one easily and quickly (at least, as much as possible).

Infrastructure and installation

I will be assuming that you are running a server which is accessible on the internet.
Chances are, if you’re reading this, you can probably figure out how to do that at home or rent a server.
A lot of things will have to remain unexplained. I am trying to make this as easy to read for beginners as possible, but explaining more would turn this post into a 10,000 words long essay.
That server will be running a Linux-based distribution called Debian, with the current (as of writing this) stable version (number 10).
Your server probably does not need to match my distribution, since we will be compiling the IRC server.
Note that pre-compiled binaries are available for Windows.
Ideally, IRC servers should be online all day long.

Different implementations of the IRC protocol server-side have different additional features and vary a lot in terms of configuration.
This might be due to the tendency to build upon the current standard (and its extensions) to add more user-friendly features.
I will be using UnrealIRCd since it is the one I have most experience with.

Installing and compiling

Creating the IRC user

For security reasons, it is recommended that a user without administrator privileges download, compile and run the software.
The first thing we will do is create a system user called irc who is just a plain rando for the system.
I will also give this user a home in a particular place in the system. As an admin, run

useradd irc --home-dir /opt/irc
cp -r /etc/skel /opt/irc
passwd irc

The first command tells the system that a new user called irc with home directory /opt/irc should be created.
The second command copies a template for user home directories at /opt/irc.
The last command gives a prompt to set the password for that user.

Switch to the irc user and move to its home directory.

su irc

Downloading the sources

We must then download the source code of the IRC server software.
The download page of UnrealIRCd provides some options, and I will be using the latest release (5.0 branch).

wget -c https://www.unrealircd.org/downloads/unrealircd-
tar zxvf unrealircd-

As indicated on the download page, you may want to check the integrity of the downloaded archive before opening it.
I have done so using the sha256 hash function implemented by sha256sum.


Next, you may want to open the UnrealIRC documentation alongside this page and check things out as we go.
Specifically, open the page called “Installing from source“, and proceed along with me.

Our layout will be as such : in /opt/irc/, the folder called unrealircd-5.0.<something> will contain the source code, and a folder called unrealircd will contain configuration files, binaries, and such.

Enter the source code folder and execute the configuration binary

cd unrealircd-

Press the space bar to skip the text.
Follow the indications of the configuration binary to set some important values that will be used to compile the IRC server, including :

  • Potential previous installations (we’ll leave it empty)
  • Installation directory (keep the default value, i.e. /opt/irc/unrealircd)
  • File permissions for the installed files (0600 is fine, it means “only the person who owns this directory can read and write these files”)
  • Path to OpenSSL library (people who know what they’re doing use this; I don’t)
  • Inclusion of remote configuration files, which is useless in our case.
  • Prefixes for channel administrators, owners and such. This is useful because it can help you tell apart who operates, moderates and controls a channel.
  • Nickname history length
  • Number of sockets (and file descriptors). The automatic setting is good.
  • Any other custom parameters

When you approve the last one, a series of tests should fly by, eventually telling you if something is missing.
Note that you will at least need to install a C compiler like the Gnu C Compiler (GCC), as well as the Binary Utilities (binutils) suite (all of them available using standard package managers).

When tests and module compilation is done, the questions resume, asking us if we want to use a SSL certificate.
This is more or less mandatory, since the IRC protocol does not require the implementation of specific encryption mechanisms between the server and the client, or between servers.
Without this encryption, sensitive content, like message content (including potential passwords and sensitive information) and aggregation information (shared between servers) would then be sent unencrypted and would be trivial to gather for anyone familiar with packet sniffing.

The prompts for the SSL certificate generation are very easy to understand.
Note that it does not matter what “Common name” (or “Fully Qualified Domain Name (FQDN)“) you use since this is a self signed certificate (even if you have a FQDN, you’d have to issue a certificate verification request).
You will have to tell your users to trust the certificate, since it will be marked as invalid, because no certificate authority has verified its validity.
In order to get a valid, certified ssl certificate (note that it requires a FQDN point to your server), you might want to check out Let’s Encrypt.

When this is done, the configuration binary tells you its final, ominous words : “Type ‘make’ and let it compile”. Do so

make -j3
make install

The -jX flag (essentially) tells make to split tasks onto X different processor cores.
This can be ignored, but with most computers having many cores nowadays, being able to cut your compilation time by two or three is good to take.
The second command copies everything that was compiled by the first one into a new folder, /opt/irc/unrealircd.

Configuring the IRC daemon

Configuring an IRC server is a tough job. With normal setups, you’d likely want to spend a lot of time reading the complete configuration page.
We don’t really have that time.

Working with the example configuration

Thankfully, as described in the installation guide, a standard configuration ships with UnrealIRC.
It only needs some tweaking. We’ll get started with that.

cd /opt/irc/unrealircd

The directory conf contains configuration files.
You can use the example file conf/examples/example.conf and copy it to become the main configuration file.

cd conf/examples/example.conf conf/unrealircd.conf

Reading the syntax guide will help if you’re not used to this style of block-based configuration.

Open the file with your favourite editor, and let’s walk through it. A lot of it is just default stuff required by the IRC protocol.
Other blocks define important informaiton we need to change.

A note on configuration

Some things in the configuration of a server are unique to it. Others must be common within a network.
I will configure a server at address irc.example.com, with standard ports 6667 (unencrypted), 6697 (encrypted), 6900 (server exchange).
It will belong to a network called ExampleIRC. It will have server ID 001.

My main administrator will be nicknamed Malo, with username malo and host

The ME block

The ME block describes basic information about one server within the network.
Here is the documentation for that block.
We will set the following values :

  • name is the name of the server within the network. It servers to identify the server within the network and, ideally, it is also its address.
  • info should be a string describing the network’s name, and should be common to all servers in the network.
  • sid is the server identifier, which should be a unique number for every server in the network.

In our case, we end up with the following me block.

me {
name "irc.example.com";
info "ExampleIRC";
sid "001";

The ADMIN block

The ADMIN block is described here.
It gives basic information about the administrator of the server.
This is just meant for users to be able to reach out to administrators, and can be set to anything if you wish for more.. anonimity.

admin {
"Malo Anonymous";

The CLASS blocks

Entities that will connect to the server are described by CLASS blocks.
Each one of them describes a class, or groupe of users with different network restrictions and permissions.
The default configuration defines a class for standard human users (clients), one for human operators (opers, more on that later) and one for server entities, typically other servers in the network (servers).

The default config is fine.

The ALLOW block

The ALLOW block describes connection permissions for every one of the classes defined before.
It might be useful to familiarize yourself with the concept of wildcard characters and the typical user identification pattern, or ‘mask’ :


In my case, I allowed devices connecting from my local network (addresses in 10.8.X.X) to have 20 different connections.

allow {
ip *@10.8.*;
class clients;
maxperip 20;

The OPER block

IRC servers can benefit from some moderation tools. It is possible to create multiple levels of administrators and moderators.
Their privileges and exact authorizations are defined in another file (conf/operclass.default.conf to be specific), but, generally, operators can move users into channels, kick them from the server, or ban them from the server.
They’re the second degree of server moderation after channel operators, which we may talk about when discussing services.

Every IRC operator has a line of information that defines their name, what ‘mask’ they must match, what class of administrator they belong to, and a password.
Everything is explained here.

In our case, we would have

oper Malo {
class opers;
mask *@*;
//password "never write passwords plaintext";
password "$argon2id$v=19$m=8192,t=3,p=2$dr/J9YWaAcOaETsbAxIEGQ$iAv3cnU4tTzDgPjI2m7CzFzIy4iZeyB7enZqNtz3+aA";
operclass admin;
swhois "is an Administrator";
vhost admin.example.com;

After the operator has successfully obtained their privileges using the /oper command, whenever users will query information about them, the line Malo is an Administrator will be added, and their IP will be replaced by the “virtual” host admin.example.com.

If you want to be at least somewhat secure in your installation, use the unrealircd binary to get a hashed version of your operator password, and paste it where the “test” string is.

./unrealircd mkpasswd

After entering the password, you’ll get a string starting with $argon2id$v= as shown above.
This is what will be written in the file instead of your password.

The LISTEN block

In order for the server to be reachable, it must know what port to listen for incoming connections on.
The LISTEN block defines the different ports that will be listened on.
Typically, port 6667 is used for non encrypted communication, and port 6697 is used for encrypted communication.
However, a lot of internet firewalls do not allow ports outside of the standard 80 and 443 (web and encrypted web) to be used, so if you happen to need to change that, and you know what you are doing, this is where you change it.

// No options = no encryption, accept human clients
listen {
ip *;
port 6667;

// Option TLS = encryption, accept human clients
listen {
ip *;
port 6697;
options { tls; }

// Options TLS and serversonly = encryption, and only other servers
// in the network can use this port to exchange information
listen {
ip *;
port 6900;
options { tls; serversonly; }

Notice how we did not restrict which IP address could access what port.
It is possible to do so if you want to, by replacing the wild card character by an expression like 10.8.*.

An IRC network is made of multiple servers, but they need to know who they are, and be sure they are connecting and exchanging with a legitimate peer.
This is the purpose behind the LINK block. A server has as many LINK blocks as it does connections
to other servers. Typically, you will want to have linked servers use the same kind of IRC daemon, or, at least, know how to make different ones function.

Comment both examples with // comment markers at the beginning of each line, for now.

The ULINE block

Historically, the configuration files for IRC daemons were simple text files where one single line contained a full instruction of configuration, and began with a
letter identifying it. The jargon of IRC kept shadows of that outdated configuration system in the name of some rules (K-Line, G-Line, U-Line, etc).

The ULINE block tells your server that other servers should have more powers, and give them orders.
This is usually the case of IRC services, which run by pretending to be servers and communicating with other servers.

This is useless for now but keep it in mind when you want to set up services.

The DRPASS block

One of the privileges enjoyed by administrators and some IRC operators is the ability to request for the server to restart or stop (in tech linguo, ‘die’).
The /restart and /die IRC commands are designed to do so, and they each take a password that is set in the DRPASS block.

drpass {
restart "again, never put passwords";
die "in plain text like that";

Note that you can use hashed passwords like before, so that if your configuration file is accessed by malicious individuals, you’re not (completely) dead in the water.

The LOG block

Server administrators often use logs to diagnose issues and monitor the well being of the services they run.
The LOG block tells our server what to write and where to keep track of what it is doing.

The default configuration is fine.

The BAN block

These are pretty explicit. They are meant to prevent someone (or something) from connecting if it is a known malicious actor.
The entity trying to connect is matched with a pattern, and banned if it matches, with a given reason.

These BAN blocks work for nicknames, users, IP addresses, versions, real names, and servers.
They all descend from the Z, G, K and GZ lines of olden IRC configurations.
When a users needs to be banned, IRC operators will usually run /kline or /gline rather than add lines in there.

It is recommended to keep the ban of any user attempting to impersonate ChanServ in place. All others can be commented.

Blocks underneath describe exceptions to these bans, for testing purposes or for legitimate exceptions.

Other blocks

One particularly obscure feature of IRC is the set of actions known as Direct Client-to-Client (DCC).
It actually allows client to share files, chat, and request information on each other without having to rely on the server or network they are connected to.

Some rules can be put in place to block some DCC features, like file sending (typically to stop bots from spreading virus files).
It is also possible to deny things to users, like channel names, client versions, or server links. This is done with a DENY block.

Users can acquire “virtual hostnames” or “vhosts” that are meant to mask their real IP address using some string of text (nowadays, most ISPs will provide a host name that sort of hides it, but still).
Typically, operators get one to hide their IP address.
Virtual hosts are protected using a login and a password, so, for example, someone whose original mask matches *@some-reverse-proxy.net can execute /vhost cat goes-meow to replace that host name with very.cool.hostname.

vhost {
vhost very.cool.hostname;
mask *@some-reverse-proxy.net;
login "cat";
password "goes-meow"; // Again, don't use plain text passwords

UnrealIRC has a feature where the server can ask a DNS blacklist if an IP or domain name is known for some kind of activity.
Some of there blacklists record Tor output nodes, open proxies, drones / spammers, attackers, and so on.
I have never used it, and I don’t know what side effects it may have. I will disable it.

The SET block

Finally, we reach some important blocks that give general but detailed information on the server.
The SET block is particularly complicated to configure, but the example provided works fine.
The following values are set :

  • network-name must be identical for all servers in the network
  • default-server is the name of the default server in our network
  • services-server is the name of the services server, which we haven’t deployed (yet?)
  • stats-server can be left out when there is no statistics bot
  • help-channel is the default channel where help can be sought
  • hiddenhost-prefix is a prefix of three or four letters used to hide the host name of users who request it (and clients nowadays usually do). It should be common to the network in order to hostname bans to work.
  • prefix-quit is the string shown to users when a person disconnects from the server.
  • cloak-keys are three random chains of characters used to derive pseudo-random hostnames that hide users’ real host name. Follow the comments to set these.
  • kline-address is a URL/email address shown to users upon being banned. It’s typically meant to receave appeals or explain reasons they were banned.
  • modes-on-connect is the modes a user receives whenever connecting. Note that +x, or “cloak hostname” is therefore enabled by default.
  • modes-on-oper same as modes-on-connect for people who become IRC operators.
  • modes-on-join same but for people who “join” channels. The meaning of all these modes is found here.

In my example, we will have

set {
network-name "ExampleIRC";
default-server "irc.example.com";
//services-server "services.example.com";
help-channel "#help";
hiddenhost-prefix "ExI";
prefix-quit "Quit";
cloak-keys {

The remainder of the settings are fine by default and relate to user control and quality of service matters.

Running the server

Assuming everything was configured properly, and your ports are available to the global internet, you can now try and run the server.
As irc, tell the server to start.

./unrealircd start

Statistically you’ll have messed up something with the configuration. The information displayed on screen will tell you what and where.
If everything works, the binary will say “UnrealIRCd started” and give you back the prompt. This is normal.
The server is running. You can verify by using an IRC client and connecting to it using your address and the port you defined.

More things to set, still

Right now you have a very bare bones server. It is functional, but it is not very handy. A lot of things have to be changed.

  • The default operator we created above cannot stop or restart the servers, only update the configuration (using /rehash). See the operclass privilege list or informations on what privileges to add or what class to use.
  • The server has no “Message of the Day” file (located at conf/ircd.motd), which shows users a greeting message
  • There are no user services. Channels will cease to exist alongside all the information (modes, passwords, descriptions) surrounding them.
  • The server needs to be restart manually in case of crash, or when you reboot the computer.
    And many other things.

Even though it is bare bones, it will function as a boilerplate for you to set up everything else. I’d recommend reading the security page on secure practices as well.

Changing the lone operator’s permissions

We want our only operator to have a lot more control over the server, and, especially, to be able to kill and restart it.

We will change the admin operator class to allow every admin to use /die and /restart. This may not be the best solution long term, but with a real infrastructure, you will be able to make better decisions regarding what classes get what privileges. Open conf/operclass.default.conf.

operclass admin {
permissions {
channel { operonly; see; override { flood; } }
self { getbaddcc; opermodes; set; }
server { opermotd; info; close; module; dns; rehash;
remote; description; addmotd;
addomotd; tsctl { view; } }

Adding permissions::server::die and permissions::server::restart allows admins to use both commands shown above.

Enabling automatic restart

Ideally, if you server is meant to run while you are not watching over it, you would want it to restart when the computer does as well. Under a standard stable Debian system, this can be done using CRON (whichever implementation happens to be installed on your system). Add the following line to your cron file (run crontab -e and chose an editor) :

@reboot        /opt/irc/unrealircd/unrealircd start

And voilà. Whenever the server reboots, the server will restart!

Conclusion.. not really

However documented this post is, it is nowhere close to covering everything I wanted to cover. It is extremely basic, and you only get a boiler plate server that is just functional enough for basic moderation, and chat. Users who will do more than simply chatting will have to learn about the basics of IRC, channel and user modes, simple moderation commands, etc. People who are brave enough to follow this and have questions can throw me an email using the icon down at the bottom of this page.

I don’t know how useful this will be. I don’t know what people will want to use IRC for, if at all. Deploying a functional server is a fun project. You can learn a lot of things if you are a hobbyist sysadmin like I was when I first worked through it all (and still am today).

All I ask is that you use this knowledge for good, for your communities, for your people.