Shared Server · Rex Docs
Shared Server
The shared server is one Rex server that runs as the rex system account, as rex.service. Members of the rex group can connect to it, and every program they start runs as rex. It’s the simplest server mode for a few people who are comfortable sharing one account.
Access to the shared server is access to the rex account. Anyone in the group can see and type into every session on it and start programs as rex. Only add people you would trust with that account. If people need their own files and permissions, use the login server instead.
Starting the Server
The package creates the rex account with nologin as its shell, and Rex starts each terminal with its account’s shell. Give the account a real shell first, or every terminal on the shared server exits as soon as it starts:
sudo usermod --shell /bin/bash rex
Alpine has neither usermod nor bash by default. Install usermod with the shadow package and use /bin/sh:
apk add shadow
usermod --shell /bin/sh rex
FreeBSD has no bash by default either. Use pw and /bin/sh there:
pw usermod rex -s /bin/sh
The account stays locked, with no password, so nobody can log in as rex this way.
Then start the service now and at every boot:
sudo systemctl enable --now rex.service
On Alpine, run rc-update add rex, then rc-service rex start. On FreeBSD, run sysrc rex_enable=YES, then service rex start.
To start the server only when someone first connects, enable rex.socket instead. See Starting on Demand.
The server listens on /run/rex/server.sock and keeps its data in /var/lib/rex, the rex account’s home directory. On FreeBSD, those are /var/run/rex/server.sock and /var/db/rex.
Starting on Demand
Rex can be paired with systemd to automatically start the shared server when a client connections, instead of at boot using rex.socket.
When this is enabled, systemd creates /run/rex/server.sock and waits. When a client connects, systemd starts rex.service with the same command line and hands it the socket. The client waits a moment while the server starts, then works as usual. Access is the same: members of the rex group can use the socket.
Enable the socket instead of the service:
sudo systemctl enable --now rex.socket
Enable one or the other, not both. The socket starts the service when it needs to. systemctl status rex.socket shows “active (listening)” until the first client connects.
Once it starts, the server keeps running, even when nobody is using it. The socket changes when the server starts, not how long it runs.
While the socket is active, any connection starts the server, including rex server status. To check on it without starting it, ask systemd:
systemctl status rex.service rex.socket
Stopping rex.service alone lasts only until the next connection. To stop the server and keep it stopped, stop both units in one command. This ends every session on it:
sudo systemctl stop rex.socket rex.service
Stopping only rex.socket leaves the server running without its socket file, so nobody new can connect to it. Don’t use rex server stop either. It connects to the socket to check that the server stopped, which can start it again.
To switch to a server that always runs, disable both units, then enable the service:
sudo systemctl disable --now rex.socket rex.service
sudo systemctl enable --now rex.service
To switch back, stop the service first. systemd won’t listen on the socket while the server is running:
sudo systemctl disable --now rex.service
sudo systemctl enable --now rex.socket
Either switch ends every session on a running shared server.
Alpine and FreeBSD have no socket activation, so there the service is the only way to run the shared server. On NixOS, set services.rex.socketActivation. See The NixOS Module.
Giving People Access
Add each person to the rex group. The change applies the next time they log in:
sudo usermod --append --groups rex alice
On Alpine, run addgroup alice rex. On FreeBSD, run pw groupmod rex -m alice.
Their rex commands look for their own personal server by default, and start one if it isn’t running. To use the shared server instead, pass its socket with -S, or set REX_SERVER in their shell profile so every rex command uses it:
rex -S /run/rex/server.sock ls
export REX_SERVER=/run/rex/server.sock
On FreeBSD, the socket is /var/run/rex/server.sock. Inside the shared server’s own terminals, rex commands already use the shared server.
A rex command never starts a personal server at the shared server’s socket. If the shared server isn’t running, the command fails, and on Linux the error names the service to start. With rex.socket enabled, the connection starts the shared server instead. People outside the rex group get a permission error, because only the group can open /run/rex.
The service isn’t sandboxed on purpose. Sandboxing would also restrict the programs people run in it, so the rex account is the security boundary.
To reach the shared server from another computer, see Remote Access.
Configuration
The shared server reads its configuration file from the rex account’s home:
/var/lib/rex/.config/rex/init.lua
On FreeBSD, it’s /var/db/rex/.config/rex/init.lua. Only root can edit it, because the home directory is private to the rex account. After you change it, any member of the group can reload it:
sudo $EDITOR /var/lib/rex/.config/rex/init.lua
rex -S /run/rex/server.sock config reload
To keep the file somewhere else, pass --config PATH in a drop-in, as below.
Customizing the Service
Package upgrades replace rex.service, so put changes in a drop-in file instead. This command creates one and opens it in your editor:
sudo systemctl edit rex.service
To change the server’s options, clear ExecStart and set it again. This example raises the session limit:
[Service]
ExecStart=
ExecStart=/usr/bin/rex server run --listen /run/rex/server.sock \
--state-dir /var/lib/rex --socket-mode 0660 --log-format text \
--max-sessions 128
On Alpine, upgrades replace /etc/init.d/rex, so set command_args in /etc/conf.d/rex instead. The package never creates or changes that file. The same example there leaves out /usr/bin/rex:
command_args="server run --listen /run/rex/server.sock \
--state-dir /var/lib/rex --socket-mode 0660 --log-format text \
--max-sessions 128"
Keep the four options from the original command line. The same pattern adds a network listener for Remote Access or a block plugin directory with --block-dir. Run rex server run --help for every option.
With rex.socket, the same drop-in applies, because the socket starts rex.service. The socket’s path and mode come from the socket unit, so to change them, edit both units. See Customizing the Socket.
On FreeBSD, upgrades replace /usr/local/etc/rc.d/rex but never change /etc/rc.conf, so set rex_flags there instead. It adds options to the service’s command line rather than replacing it, so leave out the four it already has:
sysrc rex_flags="--max-sessions 128"
service rex restart
Customizing the Socket
With rex.socket, systemd creates the server’s socket and hands it to the server, which serves it instead of creating its own. Package upgrades replace rex.socket too, so put changes in a drop-in for it:
sudo systemctl edit rex.socket
The server serves only the sockets its command line names. Every ListenStream= path in the socket unit needs a matching --listen or --extra-listen in rex.service. Otherwise the server refuses to start, and clients wait until they time out.
To move the socket to /run/rex/rex.sock, stop both units first. systemd removes the socket file it knows about when the unit stops, so stopping after the edit would leave the old file behind:
sudo systemctl stop rex.socket rex.service
Then run sudo systemctl edit rex.socket and set the new path. The empty ListenStream= clears the one in the unit:
[Socket]
ListenStream=
ListenStream=/run/rex/rex.sock
Run sudo systemctl edit rex.service and set the same path:
[Service]
ExecStart=
ExecStart=/usr/bin/rex server run --listen /run/rex/rex.sock \
--state-dir /var/lib/rex --socket-mode 0660 --log-format text
Then start the socket again:
sudo systemctl start rex.socket
Clients then connect with rex -S /run/rex/rex.sock. To serve a second socket, add another ListenStream= line to the socket unit and a matching --extra-listen unix://PATH to the service. A socket for Tailscale Serve needs tailnet-serve:// instead, as Tailscale Serve shows.
The server keeps the mode that SocketMode= gives the socket, and ignores --socket-mode for it. It refuses a mode that lets every user on the machine connect, such as 0666, so keep it at 0660 or narrower. 0600 works, but locks out the rex group.
The server takes only listening stream sockets, from ListenStream=, and refuses Accept=yes. A TCP ListenStream= needs --extra-listen with the same port and --allow-anonymous, as Plain HTTP describes. Give it an IP address, not a host name.
To apply any later change to rex.socket, stop both units and start the socket again, as above. sudo systemctl restart rex.socket fails while the server is running, and leaves the server without its socket file.
Logs
The shared server logs to the journal:
journalctl -u rex.service -f
Alpine has no journal, so there the server logs to a file:
tail -f /var/log/rex/server.log
On FreeBSD, the server logs to syslog, which writes to /var/log/messages:
grep ' rex\[' /var/log/messages\
The logs are plain text. For JSON, change --log-format text to --log-format json in a drop-in.
With rex.socket, the server still logs as rex.service. If it refuses to start, such as for a socket it isn’t configured to serve, the journal names the socket and the option to add. Clients aren’t refused meanwhile. Each one waits until its --timeout runs out, 10 seconds by default.
rex server logs shows only servers started with rex server start, not the service. Use the journal instead.
Upgrading
Upgrading the package restarts a running shared server, so it runs the new version right away. On FreeBSD, pkg restarts nothing, so the server keeps the old version until you run service rex restart.
With rex.socket, a running server restarts the same way, and one that hasn’t started yet runs the new version when the first client connects. Upgrades never restart rex.socket itself. If a new version changes the socket unit, the change applies once you stop both units and start the socket again, or at the next reboot.
Restarting ends every session and every program in them. Upgrade when nobody has work running, or hold the package until then. The login server doesn’t have this problem. Its sessions survive upgrades. Upgrading covers the rest.