Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


Shadowsocks server setup begins with a specific implementation and a matching client key, not a universal configuration copied between projects. This guide pins shadowsocks-rust, uses one documented 2022 method, and separates a local application proxy from full-device traffic coverage.
Key Takeaways
- Keep implementation, method, key, endpoint, and transport aligned.
- A 2022 key is not an arbitrary memorable password.
- Test application TCP and UDP independently, including wrong-key rejection.
- Rotation of this shared key revokes every client using the old value.
Use the self-hosting ownership guide to plan independent administration and recovery. Before choosing traffic scope, compare a full tunnel with an application proxy. This example changes only the explicitly configured test application and a controlled UDP forwarding listener. It does not install TUN, change global routes, or provide a kill switch.
The Shadowsocks overview explains the protocol boundary. Compare VPS maintenance with a managed exit if host ownership is not your goal. The paired Hysteria2 deployment has a different UDP transport and certificate requirement; do not mix its configuration fields into this procedure.
Scope and evidence: dedicated Ubuntu 24.04 LTS x86-64 server, separate authorized Linux client, public IPv4 endpoint, and shadowsocks-rust v1.25.0 on both ends. This is the Rust implementation, not Python, libev, a panel, or an unspecified client app. No end-to-end author test is reported. Expected results are reader acceptance criteria based on official documentation. Other architectures require their matching official assets.[1][2][3]
Confirm a fresh SSH connection and provider console independent of the proxy. Privately record existing listeners, routes, DNS, firewall ownership, services, and costs. Reserve server TCP/UDP 8388 and client TCP 1080 plus UDP 19001. Stop on collisions or existing shadowsocks-ops paths, unit, or account rather than adopting another service.
Install missing curl, ca-certificates, xz-utils, and Python 3 from trusted Ubuntu packages. In a new private work directory, download the exact official archive and its matching checksum. Run these commands individually, stopping on any failure; do not extract or execute before checksum success.
umask 077
mkdir shadowsocks-1.25.0-reader
cd shadowsocks-1.25.0-reader
curl --fail --show-error --location \
https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.25.0/shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz \
-o shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz
curl --fail --show-error --location \
https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.25.0/shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz.sha256 \
-o shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz.sha256
sha256sum --check shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz.sha256
The checksum checks agreement with the official release file, not an independent publisher signature. Verify project ownership and available release provenance too. Inspect the archive listing for unexpected paths, then extract as the unprivileged reader. The expected binaries are at the extraction root. If the layout differs, stop and inspect rather than guessing a privileged copy command.[1][2]
tar -tf shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz
tar -xf shadowsocks-v1.25.0.x86_64-unknown-linux-gnu.tar.xz
./ssserver --version
./sslocal --version
./ssservice genkey -m "2022-blake3-aes-128-gcm"
sudo install -m 755 ssserver /usr/local/bin/ssserver-ops
sudo useradd --system --home /nonexistent --shell /usr/sbin/nologin shadowsocks-ops
sudo install -d -o root -g shadowsocks-ops -m 750 /etc/shadowsocks-ops
Both version commands must identify 1.25.0. Keep protected copies of this archive and its verification evidence. The generator prints a secret: keep the terminal private and put the value directly into a password manager, never a public issue or shell argument.
The selected method is exactly 2022-blake3-aes-128-gcm. Its password field contains a Base64-encoded 16-byte key, generated for this method. Ordinary AEAD password derivation and 2022 keys are different contracts. Do not substitute a human password, a key for the 256-bit method, a colon-separated multi-user form, or another project's default.[2][4]
Create /etc/shadowsocks-ops/server.json using a root editor, replacing the placeholder with the generated key. The placeholder is intentionally not usable key material. Keep JSON syntax valid and do not put comments into this strict JSON example.
{
"server": "0.0.0.0",
"server_port": 8388,
"method": "2022-blake3-aes-128-gcm",
"password": "REPLACE_WITH_GENERATED_2022_KEY",
"mode": "tcp_and_udp"
}
0.0.0.0 is the server listener, never the client's destination. This native example does not use TLS certificates or plugins; the shared key is essential authentication material, not a certificate private key. Do not switch to none, plain, or deprecated stream ciphers to make a failing client connect.
AEAD-2022 checks timestamps as part of replay protection, so verify both hosts' clocks with their trusted time service. Diagnose clock failures rather than disabling protocol protections.[4]
Save the following dedicated unit as /etc/systemd/system/shadowsocks-ops.service. High port 8388 permits an unprivileged service user; no root network capability or privileged container is needed.
[Unit]
Description=Dedicated Shadowsocks Rust reader deployment
After=network-online.target
Wants=network-online.target
[Service]
User=shadowsocks-ops
Group=shadowsocks-ops
ExecStart=/usr/local/bin/ssserver-ops -c /etc/shadowsocks-ops/server.json
Restart=no
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
[Install]
WantedBy=multi-user.target
Keep SSH and unrelated firewall rules intact. Initially authorize only the test client's source to both TCP and UDP 8388 at the actual cloud and host firewall owners. If your approved policy permits TCP only, record UDP as unavailable rather than claiming success. Do not flush rules or expose a local SOCKS port to solve remote connectivity.
sudo chown root:shadowsocks-ops /etc/shadowsocks-ops/server.json
sudo chmod 640 /etc/shadowsocks-ops/server.json
sudo systemctl daemon-reload
sudo systemctl start shadowsocks-ops
sudo systemctl status shadowsocks-ops --no-pager
sudo ss -lntup
Expect the chosen service to remain active and both intended IPv4 listeners on 8388. If startup fails, keep ingress closed and separate invalid JSON, unsupported method/key format, file permissions, architecture, clock, and port conflicts using the private service log. A successful bind proves neither key matching nor application forwarding.
On the separate Linux client, obtain and verify the same version's matching release as above, retaining its sslocal and ssservice binaries. Replace ss.example.com with the server's real reachable IPv4 address or domain; any published AAAA must match a separately designed IPv6 path. Use the same method and key as the server.
The UDP exercise also needs an authorized echo responder you control at the replacement for udp-check.example.net:9001, reachable from the server. The example name is not a public testing service. Without a responder, leave UDP acceptance unverified. Confirm the selected build supports the documented local-tunnel feature; stop on an unsupported protocol rather than changing implementations.[2]
{
"server": "ss.example.com",
"server_port": 8388,
"method": "2022-blake3-aes-128-gcm",
"password": "REPLACE_WITH_GENERATED_2022_KEY",
"locals": [
{
"protocol": "socks",
"local_address": "127.0.0.1",
"local_port": 1080,
"mode": "tcp_only"
},
{
"protocol": "tunnel",
"local_address": "127.0.0.1",
"local_port": 19001,
"forward_address": "udp-check.example.net",
"forward_port": 9001,
"mode": "udp_only"
}
]
}
The SOCKS listener is deliberately TCP-only; the separate tunnel entry is the application UDP test path. Both remain loopback-only. Start the client in its private directory and verify no unintended LAN listener appears.
chmod 600 client.json
./sslocal -c client.json
Explicitly select SOCKS5 127.0.0.1:1080 in a test browser, including proxy-side name resolution where supported. Make a fresh HTTPS request, check the visible exit IP, and compare with the server's known egress. A listener or a generic “connected” indicator is insufficient.
For the negative check, stop the client and copy its configuration privately. Generate another valid-format key with the same generator and method; change only the copy's password, preserving endpoint and method. Start that profile and make a new proxied request. It must fail to carry traffic. An invalid Base64 error before contacting the server does not prove server rejection. If traffic works, close ingress and check direct fallback, another running profile, and the server's actual key.
Return to the valid profile. Send one controlled UDP echo through its 127.0.0.1:19001 forwarding listener. The following reader command expects the identical payload from your authorized responder; no observed output is claimed.
python3 - <<'CHECK'
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(3)
sock.sendto(b"reader-check", ("127.0.0.1", 19001))
data, _ = sock.recvfrom(1024)
assert data == b"reader-check", "Unexpected UDP reply"
CHECK
| Check | Expected reader evidence | Failure response |
|---|---|---|
| Application TCP | Fresh proxied HTTPS request and intended exit | Separate browser selection, key, server egress, and destination |
| Application UDP | Controlled echo matches the sent payload | Check client tunnel, both UDP rules, server mode, and return path |
| DNS | Application resolution follows the selected policy | Inspect local versus proxy resolution independently of exit IP |
| IPv6 | Applicable application paths have explicit inclusion or exclusion | Do not treat IPv4 acceptance as device-wide protection |
| Disconnect | The test application fails or falls back as configured | Stop sensitive activity until the intended failure policy is verified |
| Open proxy | Unauthorized and wrong-key traffic cannot relay; local SOCKS stays loopback | Close ingress immediately and correct binding or authentication |
Also attempt the same controlled UDP request with the wrong-key profile: it must not return an authenticated relay reply. A timeout by itself has many causes, so keep a working valid-key control before and after the negative test. Source restrictions supplement authentication; if every blocked request is stopped by the firewall, it does not independently prove key rejection.
Restart the server during an authorized window, repeating valid and wrong-key TCP/UDP checks. Enable automatic startup only after acceptance and verification that the protected configuration remains readable after reboot.
For revocation, close this deployment's ingress, generate a new method-matched key, replace the server key, and restart to terminate existing sessions. Update only retained clients. This single-key baseline revokes all clients using the old key, not one device label. Record rotation outside the backup, then verify old-key rejection and valid-client success after another restart.
For a consistent backup, stop only this unit. The destination below must not already exist; stop if it does. Each command is conditional on the previous command succeeding. If copying fails, preserve the original files and restore its known working state deliberately, rather than deleting the source.
sudo systemctl stop shadowsocks-ops
sudo install -d -m 700 /root/shadowsocks-backup-20261011
sudo cp -a /etc/shadowsocks-ops /root/shadowsocks-backup-20261011/
sudo cp -a /usr/local/bin/ssserver-ops /root/shadowsocks-backup-20261011/
sudo cp -a /etc/systemd/system/shadowsocks-ops.service /root/shadowsocks-backup-20261011/
sudo chmod -R go-rwx /root/shadowsocks-backup-20261011
sudo systemctl start shadowsocks-ops
Keep firewall records, the client build, recovery ownership/modes, and external rotation ledger with the recovery plan. Encrypt the archive before restricted off-host storage. The backup contains an authentication key; do not publish it or leave a world-readable copy.
Recover on an isolated same-version replacement with ingress closed. Restore the binary and unit to their stated paths, recreate the service account, and copy the saved configuration directory to /etc/shadowsocks-ops. Before startup, reapply directory root:shadowsocks-ops mode 750, configuration ownership root:shadowsocks-ops mode 640, binary mode 755, and unit mode 644. Apply the current key from the separate rotation ledger, reload systemd, and start the unit. Repeat positive, negative, UDP, and restart checks before reopening; a stale archive must not revive a revoked key.
When retiring, close only this service's TCP/UDP rules, stop and disable shadowsocks-ops, stop its client, and restore the test application's prior proxy setting. Confirm local 1080/19001 listeners are gone. After retention decisions, remove only the created unit, binary, files, and user; reload systemd and review DNS/cloud resources separately. Independently verify a fresh SSH login and original services. Removal does not cancel billing; uncertain recovery means stop further changes.
No. This method requires a Base64 representation of a key with the specified length. Use the implementation's method-specific generator and keep the resulting value secret.
Implementation features and configuration contracts differ. This guide fixes Rust v1.25.0 on both ends; verify another implementation separately before mixing examples or assuming compatibility.
No. UDP needs its own supported client mode, server setting, firewall path, and controlled responder. Record its acceptance separately from the browser request.
Only explicitly configured application traffic and the demonstrated forwarding listener are covered. Whole-device routing and a kill switch require an independently designed and verified setup.
Every client shares the same server key in this baseline. Rotation affects all of them; distinct identities require a separately supported configuration and acceptance plan.
A reachable encrypted listener is not enough to establish unauthorized relay. Test wrong-key denial and local SOCKS binding, keeping valid-key controls to distinguish authentication from network failure.
No end-to-end test is claimed. Commands define reader checks; record your actual versions, authorized resources, network, results, and recovery evidence before accepting the service.
Sources:
Sources checked 11 October 2026.
Related Articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





