Exploring the roots and future of Internet Relay Chat

The Lounge: Self-Hosted Web IRC Client Done Right

I resisted web IRC clients for years. KiwiIRC was fine for guest access. Mibbit worked when nothing else did. But the idea of running IRC through a browser as my primary client felt wrong. Then a friend running a small network set up The Lounge for his team in 2019, and I grudgingly tried it. Three days later, I was checking IRC from my phone during grocery shopping. That's when I accepted that web IRC had finally gotten good enough.

Why The Lounge Exists

The Lounge started as a fork of Shout, a Node.js web IRC client that was simple but limited. The Lounge team took that foundation and built something genuinely usable: persistent connections (your client stays connected even when you close the browser), message history stored server-side, file upload support, link previews, push notifications, and a responsive UI that works on phones without wanting to throw the phone.

The core value proposition is the same as ZNC — you stay connected 24/7 and read your scrollback when you come back. But unlike ZNC + a separate client, The Lounge is the bouncer and the client in one package. You open a browser tab, you're on IRC. No client configuration. No SASL setup. No port forwarding for DCC.

For small teams and communities, this is transformative. Instead of telling 20 people to install an IRC client, configure a bouncer, set up SASL, and manage their own connection, you give them a URL and login credentials. They're on IRC in under a minute. I've seen this single change double the active user count on small networks.

The Lounge runs in two modes: private (multi-user, each with their own login) and public (single shared instance, no authentication). Private mode is what you want for a team or community. Public mode is what you'd embed on a website for guest access to a help channel.

Installation and Initial Setup

The Lounge requires Node.js. Use the LTS version — not the latest, not the oldest still supported. As of this writing, Node.js 20 LTS is the target.

# Install Node.js 20 LTS on Debian/Ubuntu
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt-get install -y nodejs

# Verify
node --version  # Should show v20.x.x
npm --version   # Should show 10.x.x

Install The Lounge globally via npm:

# Install The Lounge
sudo npm install -g thelounge

# Verify installation
thelounge --version

# Start initial setup
thelounge start

On first run, The Lounge creates its configuration directory. By default, this is ~/.thelounge/ when run as a user, or /etc/thelounge/ when installed system-wide. The configuration file is config.js inside that directory.

Create your first user:

# Add a user (interactive prompt for password)
thelounge add myusername

# The user file is stored as:
# ~/.thelounge/users/myusername.json

By default, The Lounge listens on port 9000. Open your browser to http://your-server:9000 and log in with the credentials you just created.

Tip: Run thelounge add before starting the service, not after. If you start in private mode with no users, you'll see the login page but have no way to authenticate. Create at least one user first.

Configuration Deep Dive

The Lounge's config.js file controls everything. Here's a production-ready configuration with commentary:

"use strict";

module.exports = {
    // Private mode = multi-user with authentication
    public: false,

    // Bind address and port
    host: "127.0.0.1",  // Behind reverse proxy
    port: 9000,

    // Enable HTTPS directly (or use reverse proxy)
    // https: {
    //     enable: true,
    //     key: "/etc/letsencrypt/live/irc.example.com/privkey.pem",
    //     certificate: "/etc/letsencrypt/live/irc.example.com/fullchain.pem",
    // },

    // Message history
    messageStorage: ["sqlite"],  // sqlite, text, or both
    storagePolicy: {
        enabled: true,
        maxMessages: 100000,  // Per channel
        deletionPolicy: "statusOnly",
    },

    // File uploads
    fileUpload: {
        enable: true,
        maxFileSize: 10240,  // KB (10 MB)
        baseUrl: null,  // Uses default path
    },

    // Link previews
    prefetch: true,
    prefetchStorage: true,
    prefetchMaxImageSize: 2048,  // KB
    prefetchMaxSearchSize: 50,

    // Defaults for new users
    defaults: {
        name: "Libera.Chat",
        host: "irc.libera.chat",
        port: 6697,
        tls: true,
        rejectUnauthorized: true,
        nick: "thelounge_%%",  // %% = random digits
        username: "thelounge",
        realname: "The Lounge User",
        join: "#thelounge",
        leaveMessage: "Leaving",
    },
};

The messageStorage option deserves attention. SQLite is the default and works well for most deployments. It stores messages in a database file per user, making search fast. The "text" option writes plain log files, which is useful if you want to grep logs externally. You can enable both simultaneously at the cost of disk space.

Setting host to 127.0.0.1 means The Lounge only accepts connections from localhost. This is correct when running behind a reverse proxy (which you should be). Never expose The Lounge directly on a public IP without TLS.

User Management and LDAP

In private mode, each user has a JSON file in the users/ directory. Passwords are hashed with bcrypt. You can manage users via the CLI:

# Add user
thelounge add alice

# Reset password
thelounge reset alice

# Remove user
thelounge remove alice

# List users
thelounge list-users

For larger deployments, The Lounge supports LDAP authentication. This is configured in config.js:

module.exports = {
    // ... other config ...

    ldap: {
        enable: true,
        url: "ldaps://ldap.example.com:636",
        tlsOptions: {
            rejectUnauthorized: true,
        },
        primaryKey: "uid",
        searchDN: {
            rootDN: "cn=admin,dc=example,dc=com",
            rootPassword: "ldap_bind_password",
            filter: "(&(uid={{username}})(memberOf=cn=irc-users,ou=groups,dc=example,dc=com))",
            base: "ou=people,dc=example,dc=com",
            scope: "sub",
        },
    },
};

With LDAP enabled, users authenticate against your directory. The filter line is where you control who gets access — the example restricts it to members of the "irc-users" group. User-specific settings (joined channels, theme preferences) are still stored locally in the users directory.

I set this up for a team of about 40 people at a previous job. The LDAP integration meant we didn't manage separate IRC credentials. When someone left the company and their LDAP account was disabled, they lost IRC access automatically. No cleanup needed.

Reverse Proxy and TLS

Running The Lounge behind nginx or Caddy is the standard production setup. Here's a minimal nginx configuration:

server {
    listen 443 ssl http2;
    server_name irc.example.com;

    ssl_certificate /etc/letsencrypt/live/irc.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/irc.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_http_version 1.1;
        proxy_set_header Connection "upgrade";
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 1d;  # Long timeout for WebSocket
    }
}

The WebSocket headers (Connection: upgrade and Upgrade) are mandatory. Without them, The Lounge falls back to HTTP long polling, which works but is slower and uses more bandwidth. The proxy_read_timeout 1d prevents nginx from closing idle WebSocket connections.

If you use Caddy instead, the configuration is shorter:

irc.example.com {
    reverse_proxy 127.0.0.1:9000
}

Caddy handles TLS automatically via Let's Encrypt and WebSocket proxying works out of the box. For simple setups, Caddy saves time.

Warning: If The Lounge is accessible without TLS, anyone on the network path can see your IRC credentials and messages. Always use TLS — either directly in The Lounge's config or (preferred) via a reverse proxy. This applies especially if users connect over the public internet or shared WiFi.

Mobile Experience and Push Notifications

The Lounge's web interface is responsive and works on mobile browsers. There's no native app — it's a Progressive Web App (PWA). On Android and iOS, you can "Add to Home Screen" from Chrome/Safari, and it behaves like a native app with its own icon and splash screen.

Push notifications require a service worker and are supported in modern browsers. When someone mentions your nick or sends you a private message while the tab is closed, you get a browser notification. On Android, this works reliably. On iOS, PWA push notifications were added in iOS 16.4 and are somewhat inconsistent depending on the device and iOS version.

The mobile experience is genuinely one of The Lounge's strongest points. The sidebar collapses into a hamburger menu. The channel switcher is thumb-friendly. The input field adapts to the keyboard. I've had full IRC conversations from my phone while waiting for trains, which is something I never managed comfortably with ZNC + a mobile IRC client.

File uploads from mobile work too — you can share photos directly from your camera roll into an IRC channel. The files are stored on your The Lounge server, and a link is posted in the channel. This means the files persist as long as your server does, and other users can access them without DCC.

Themes and Customization

The Lounge ships with several themes and supports custom CSS. Users can pick their theme from the client settings. Administrators can add custom themes as npm packages:

# Install a community theme
thelounge install thelounge-theme-solarized

# The theme appears in user settings after restart
sudo systemctl restart thelounge

For branding or custom styling, you can inject CSS through the config.js:

// In config.js
module.exports = {
    theme: "default",
    // Custom CSS file served from The Lounge's public directory
    // Place your CSS in ~/.thelounge/packages/custom-style/custom.css
};

You can also write custom plugins. The Lounge's plugin system lets you add commands, modify message rendering, and integrate with external services. The plugin API is documented on the project's website, though the ecosystem is smaller than you might expect. Most customization needs are handled by themes and the built-in settings.

For a systemd service file to keep The Lounge running:

# /etc/systemd/system/thelounge.service
[Unit]
Description=The Lounge - IRC client
After=network.target

[Service]
Type=simple
User=thelounge
Group=thelounge
ExecStart=/usr/bin/thelounge start
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/thelounge
PrivateTmp=true

[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable thelounge
sudo systemctl start thelounge
sudo systemctl status thelounge

With ReadWritePaths limited to the data directory and ProtectSystem=strict, The Lounge runs in a reasonably sandboxed environment. This won't stop a determined attacker with an RCE, but it limits the blast radius of a misconfiguration.

FAQ

Can The Lounge replace ZNC?

For most users, yes. The Lounge maintains a persistent connection to IRC servers and stores scrollback, which is ZNC's primary function. The difference is that The Lounge is also the client, while ZNC is just a bouncer that requires a separate client. If you specifically need to connect multiple different clients (e.g., WeeChat on desktop and a mobile app) to the same IRC session, ZNC is more flexible. For a single-interface setup, The Lounge is simpler.

How much server resources does The Lounge need?

A small deployment (5-10 users, a few networks each) runs comfortably on a VPS with 1 GB RAM and 1 CPU core. Node.js memory usage grows with the number of connected users and stored messages. The SQLite database can grow large if you store years of history — plan for a few GB of disk per active user. Monitor memory usage after deployment and adjust limits as needed.

Is The Lounge secure enough for production use?

The Lounge has had security audits and the team responds to vulnerability reports. Run it behind a reverse proxy with TLS. Keep Node.js and The Lounge updated. Use strong passwords or LDAP. Restrict access with firewall rules. With these basics, it's suitable for team and community use. For high-security environments, evaluate the current CVE history and decide based on your threat model.

Can I migrate from a KiwiIRC or Glowing Bear setup?

There's no automated migration tool. You'll need to recreate user accounts and network configurations. Channel history won't transfer. The good news is that The Lounge's setup is straightforward enough that manual migration for a small team takes less than an hour. For large deployments, script the user creation using the CLI.