🐳 20x Errors in AmneziaVPN (202, 203)
Error 202 (ServerContainerMissingError) means Amnezia could not build or start the Docker container with the protocol. Error 203 (ServerDockerFailedError) means Docker itself is missing on the server. Both are fixed from the console, and everything below is step by step.
How to read this page
You do not need to do all of it. Follow the order of operations at the end of the page — it tells you which section is yours. Commands run over SSH on the server.
Docker Hub mirrors
The most common cause of 202: images do not download, so the container is never built. Start here.
Step 1. Check Docker Hub availability
docker pull hello-worldNo error — go to the unstable Alpine CDN section. An error (TLS handshake timeout, access denied, unauthorized) — carry on.
Step 2. Configure mirrors
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://dockerhub.timeweb.cloud",
"https://docker.m.daocloud.io",
"https://docker.1panel.live",
"https://hub.rat.dev"
],
"dns": ["8.8.8.8", "1.1.1.1"]
}
EOF
systemctl restart dockerKnown mirrors worth trying — not all are stable, and some may be unreachable from a given host: docker.1ms.run, dockerhub.timeweb.cloud, docker.m.daocloud.io, docker.1panel.live, hub.rat.dev, dockerproxy.com, docker.nju.edu.cn.
Step 3. See which mirrors answer from this server
for host in dockerhub.timeweb.cloud docker.m.daocloud.io docker.1panel.live hub.rat.dev docker.1ms.run; do
echo "=== $host ==="
curl -sI --max-time 5 https://$host/v2/ && echo "OK" || echo "FAILED"
doneNote which domains end in OK and which in FAILED.
Step 4. Keep only the working mirrors
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.1panel.live",
"https://hub.rat.dev"
],
"dns": ["8.8.8.8", "1.1.1.1"]
}
EOF
systemctl restart dockerWhy this matters: if both working and dead mirrors stay in the config, Docker still tries each in turn and wastes time on the dead ones. Keeping only the verified ones makes builds faster and steadier.
Step 5. Re-check the pull
docker pull hello-world
docker pull alpine:latestIf it succeeds, go back to the AmneziaVPN app and install the protocol.
Recovering from a wrapper failure
This section is for you if you installed a wrapper over Docker — following the instructions below or a third-party guide — and ran mv /usr/bin/docker /usr/bin/docker.real twice. Symptom: the server hangs and docker ps and docker run do not respond.
Cause: a second mv /usr/bin/docker /usr/bin/docker.real renames the wrapper itself, leaving two identical scripts calling each other in a loop.
Check — many docker.real processes eating CPU:
ps aux | grep -i dockerRecovery:
pkill -9 -f "docker.real"
ps aux | grep -i docker
rm -f /usr/bin/docker /usr/bin/docker.real
apt install --reinstall -y docker.io
file /usr/bin/docker
docker ps -aOnce Docker is restored you can install the wrapper again — but check file /usr/bin/docker first so it is not layered over itself.
Unstable Alpine CDN
Symptom: docker pull hello-world works — yet 202 still appears, sometimes reproducing and sometimes not.
Check:
for i in 1 2 3 4 5; do
docker run --rm alpine:3.19 sh -c "apk update" 2>&1 | tail -3
sleep 1
doneIf you see IO ERROR or Permission denied even once, the network path to dl-cdn.alpinelinux.org (Fastly) is unstable. MTU and IPv6 are usually not involved.
1. Separate out the real Docker, if no wrapper is in place yet:
file /usr/bin/docker
mv /usr/bin/docker /usr/bin/docker.realRun mv exactly once
If file reports ELF 64-bit LSB executable, that is the real Docker and mv is safe. If you see Bourne-Again shell script, a wrapper is already installed: do not run mv — go straight to step 2. Running it again hangs the server, and then recovery is what helps.
2. Build the image manually, retrying until it succeeds (usually 5–25 attempts):
for i in $(seq 1 30); do
docker.real build --network=host --no-cache --pull -t amnezia-awg2 /opt/amnezia/amnezia-awg2 && break
done3. Save it as a permanent backup:
docker.real tag amnezia-awg2:latest amnezia-awg2-backup:latest4. Install a wrapper that substitutes the backup instead of building:
cat > /usr/bin/docker << 'EOF'
#!/bin/bash
CMD="$1"
if [ "$CMD" = "build" ]; then
shift
TAG=""; prev=""
for arg in "$@"; do [ "$prev" = "-t" ] && TAG="$arg"; prev="$arg"; done
if [ -n "$TAG" ] && /usr/bin/docker.real image inspect "${TAG}-backup" >/dev/null 2>&1; then
/usr/bin/docker.real tag "${TAG}-backup" "$TAG"
exit 0
fi
for i in $(seq 1 30); do
/usr/bin/docker.real build --network=host "$@" && /usr/bin/docker.real tag "$TAG" "${TAG}-backup" 2>/dev/null && exit 0
done
exit 1
elif [ "$CMD" = "run" ] || [ "$CMD" = "create" ]; then
shift
ARGS=(); SKIP=0
for arg in "$@"; do
[ "$SKIP" = "1" ] && { SKIP=0; continue; }
[ "$arg" = "--sysctl" ] && { SKIP=1; continue; }
[[ "$arg" == --sysctl=* ]] && continue
ARGS+=("$arg")
done
exec /usr/bin/docker.real "$CMD" --network=host "${ARGS[@]}"
else
exec /usr/bin/docker.real "$@"
fi
EOF
chmod +x /usr/bin/dockerCheck: time docker build -t amnezia-awg2 /opt/amnezia/amnezia-awg2 should finish in a fraction of a second. Then install the protocol from the app as usual.
The protocol installed, but there is no internet
The client says “Connected” while no traffic flows. Three checks:
Port
docker exec <container> cat /opt/amnezia/awg/awg0.conf | grep ListenPort
ss -ulnp | grep amneziawgIf the port in the config differs from the one actually being listened on, the client config is stale: with --network=host the -p flag in docker run is ignored and the port is set only inside awg0.conf.
Fix: remove the protocol or the server in the app and install again — a consistent config will be generated.
NAT
iptables -t nat -L POSTROUTING -n -vIf the MASQUERADE rule sits on eth0/eth1 with a zero packet counter while the real interface has another name (ip link show, e.g. ens1), the rule will never fire.
iptables -t nat -A POSTROUTING -s 10.8.1.0/24 -o ens1 -j MASQUERADE
iptables-save > /etc/iptables/rules.v4 2>/dev/null || netfilter-persistent save 2>/dev/nullSubstitute your own subnet and interface.
Forwarding
sysctl net.ipv4.ip_forward # should be 1If it is 0:
sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.confError 203: Docker is not installed
Diagnostics:
which docker
docker --version
systemctl status dockerIf which docker is empty or docker --version says command not found, Docker is simply absent:
curl -fsSL https://get.docker.com | shThe whole order of operations
- Is Docker there at all? If
docker --versionsayscommand not found, install it: error 203. docker pull hello-worldfails — configure the Docker Hub mirrors. If it succeeds, skip that section.- Installation fails intermittently — run the
apk updateloop, and if you catch errors, install the caching wrapper: unstable Alpine CDN. - The protocol installed but there is no internet — check the port in the config, the NAT rule on the correct interface, and
ip_forward: the protocol installed, but there is no internet. - The server hung after working with the wrapper — recovery.
Still no luck? Write to us and we will work it out together: Contacting Support.
