Sending RCON Commands from the Linux Command Line

March 12, 2026 Daemon666 9 min read

Being able to fire an RCON command at your CS 1.6 server from a shell — changelevel, amx_ban, a config reload — is enormously handy for scripts, cron jobs and quick fixes without launching the game. But GoldSrc RCON is not the TCP protocol that Source-era tools like mcrcon speak; it is a UDP challenge-response scheme, and using the wrong tool just times out. This explains the protocol and gives you a working client.

1. Know the protocol (why generic tools fail)

GoldSrc RCON runs over the same UDP game port (default 27015) and works in two steps:

  1. The client sends a getchallenge request. The server replies with a one-time challenge number.
  2. The client sends rcon <challenge> "<password>" <command>. The server runs it and returns the console output.

Every packet is prefixed with four 0xFF bytes. Because it is UDP with a challenge, TCP-based RCON tools (built for Source or Minecraft) cannot talk to it — that is the number-one reason people think "RCON is broken."

2. Set it up on the server

RCON must be enabled with a password in server.cfg:

rcon_password "USE-A-LONG-RANDOM-STRING"

Because RCON is brute-forceable over the network, make the password long and random, and never expose the game port to the whole internet if you can restrict it — see which ports to open. On ReHLDS you also get failed-attempt limits worth enabling.

3. A self-contained Python RCON client

This implements the two-step handshake with only the standard library:

#!/usr/bin/env python3
import socket, sys

def rcon(host, port, password, command):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(4)
    s.connect((host, port))

    # 1) get a challenge
    s.send(b"\xFF\xFF\xFF\xFFgetchallenge\n")
    reply = s.recv(1400).decode("latin-1", "ignore")
    # reply looks like: \xFF\xFF\xFF\xFFchallenge rcon 1234567890
    challenge = reply.split("rcon")[1].strip()

    # 2) send the command
    pkt = 'rcon %s "%s" %s' % (challenge, password, command)
    s.send(b"\xFF\xFF\xFF\xFF" + pkt.encode("latin-1") + b"\n")
    return s.recv(4096).decode("latin-1", "ignore")

if __name__ == "__main__":
    host, port, pw = sys.argv[1], int(sys.argv[2]), sys.argv[3]
    cmd = " ".join(sys.argv[4:])
    print(rcon(host, port, pw, cmd).lstrip("\xFF").lstrip("l"))

Save it as rcon.py, make it executable, and call it:

chmod +x rcon.py
./rcon.py 127.0.0.1 27015 "your-rcon-password" status
./rcon.py 127.0.0.1 27015 "your-rcon-password" changelevel de_inferno

The reply is prefixed with 0xFF 0xFF 0xFF 0xFF l; the script strips that so you get clean console text.

4. Useful things to run

./rcon.py HOST 27015 PW "status"              // players, map, uptime
./rcon.py HOST 27015 PW "amx_map de_nuke"     // change map via AMXX
./rcon.py HOST 27015 PW "exec server.cfg"     // reload config
./rcon.py HOST 27015 PW "amx_reloadadmins"    // re-read users.ini

Quote a command with arguments as one shell argument if it contains spaces you want kept together, e.g. "say hello everyone". This is the backbone of remote admin scripting — pair it with the uptime monitor to auto-changelevel off a stuck map, or with the log parser to react to events.

5. Keep the password out of your shell history

A password on the command line lands in ~/.bash_history and in ps output. For anything recurring, read it from an environment variable or a mode-600 file instead of typing it inline, and restrict who can run the script.

Troubleshooting

  • Timeout / no reply — the UDP port is firewalled, the server is on a different port, or you are using a TCP RCON tool. Confirm the port answers a normal query first.
  • Bad rcon_password — the password is wrong or was rotated. It must match rcon_password exactly, including case.
  • Bad challenge — you reused an old challenge; each command needs a fresh getchallenge. The script does this every call.
  • Command runs but returns nothing — many commands (like changelevel) produce no console text; that is normal. Run status to confirm the channel works.
  • RCON locked out — on ReHLDS, repeated bad passwords trip the failure limit and temporarily block your IP. Wait it out and fix the password.

Verification

Run status against the server and confirm you get the player list and current map back:

./rcon.py 127.0.0.1 27015 "your-rcon-password" status

A clean hostname, map and player table means the handshake and password are correct. Then run a harmless state change like exec server.cfg and confirm from the game console that it took. Once the script works locally, you can safely wire it into cron and monitoring.

Colaboradores: Daemon666 ✦
Compartilhar: