[GH-ISSUE #3089] MTU в контейнере AmneziaWG 3.1 #1430

Open
opened 2026-10-13 20:42:20 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @pers1307 on GitHub (Aug 29, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3089

Describe the bug

When AmneziaWG 3.1 is installed on a self-hosted server through the client, the
generated /opt/amnezia/awg/awg0.conf contains no MTU directive. The server
interface therefore comes up with the WireGuard default of 1420, while the
Amnezia client always configures its own tunnel interface with 1376.

Because of this 44-byte mismatch, every server→client packet larger than 1376
bytes is dropped at the client's tunnel interface. Handshake and small packets
pass, so the client shows "Connected", but no web page ever loads.

The AmneziaWG 2.0 container on the same server did write MTU = 1376 into its
config, so the problem only appears after switching to 3.1.

To Reproduce

Steps to reproduce the behavior:

  1. Open the client, select a self-hosted server
  2. Install the AmneziaWG 3.1 protocol on it
  3. Connect from any device — status shows "Connected"
  4. Open any website in a browser — nothing loads, requests time out
  5. On the server run docker exec <awg-container> ip link show awg0 — MTU is 1420,
    while the client interface (amn0) is 1376

Expected behavior

The server-side interface MTU should match the value the client configures on
its own tunnel interface, i.e. MTU = 1376 should be written into awg0.conf
during installation, as it was for AmneziaWG 2.0.

Log files

Server side, AmneziaWG 3.1 (broken), counters after a few minutes of use:

latest handshake: 1 minute ago
transfer: 105.84 KiB received, 10.25 GiB sent

The server keeps retransmitting into a black hole — almost nothing arrives from
the client, because its requests never get answered.

Same server, AmneziaWG 2.0 with MTU = 1376 on both sides (working):

transfer: 18.90 MiB received, 4.77 MiB sent

Configuration difference:

# AmneziaWG 3.1 — broken
$ grep -c '^MTU' /opt/amnezia/awg/awg0.conf
0
$ ip link show awg0 | grep -o 'mtu [0-9]*'
mtu 1420

# AmneziaWG 2.0 — working
$ grep '^MTU' /opt/amnezia/awg/awg0.conf
MTU = 1376

Screenshots

N/A — the client UI shows a normal "Connected" state, the failure is invisible in the
UI.

Desktop (please complete the following information):

  • OS: Ubuntu 24.04.4 LTS (kernel 6.17)
  • Version: 5.0.1.5

Smartphone (please complete the following information):

  • Device:
  • OS:
  • Version: 5.0.1.5

Server (please complete the following information):

  • OS: Ubuntu 24.04, Docker, amneziawg-go 0.0.20250522

Additional context

Workaround — add MTU = 1376 to the [Interface] section of the server's
awg0.conf and apply it live:

docker exec <awg-container> ip link set awg0 mtu 1376

After this the tunnel works normally on both desktop and mobile, with no other
changes.

Verified on a test bench: with an artificially limited path MTU the exact same
symptom reproduces (small packets fine, large downloads fail), and lowering the
tunnel MTU fixes it.

Side note: the 3.1 installer also leaves I1–I5 empty, while the 2.0
container had I1 configured. The blog post announcing 3.1 explicitly states
that protocol behaviour depends on I1, so an empty default looks unintended.

Originally created by @pers1307 on GitHub (Aug 29, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3089 **Describe the bug** When AmneziaWG 3.1 is installed on a self-hosted server through the client, the generated `/opt/amnezia/awg/awg0.conf` contains no `MTU` directive. The server interface therefore comes up with the WireGuard default of 1420, while the Amnezia client always configures its own tunnel interface with 1376. Because of this 44-byte mismatch, every server→client packet larger than 1376 bytes is dropped at the client's tunnel interface. Handshake and small packets pass, so the client shows "Connected", but no web page ever loads. The AmneziaWG 2.0 container on the same server did write `MTU = 1376` into its config, so the problem only appears after switching to 3.1. **To Reproduce** Steps to reproduce the behavior: 1. Open the client, select a self-hosted server 2. Install the AmneziaWG 3.1 protocol on it 3. Connect from any device — status shows "Connected" 4. Open any website in a browser — nothing loads, requests time out 5. On the server run `docker exec <awg-container> ip link show awg0` — MTU is 1420, while the client interface (`amn0`) is 1376 **Expected behavior** The server-side interface MTU should match the value the client configures on its own tunnel interface, i.e. `MTU = 1376` should be written into `awg0.conf` during installation, as it was for AmneziaWG 2.0. **Log files** Server side, AmneziaWG 3.1 (broken), counters after a few minutes of use: ``` latest handshake: 1 minute ago transfer: 105.84 KiB received, 10.25 GiB sent ``` The server keeps retransmitting into a black hole — almost nothing arrives from the client, because its requests never get answered. Same server, AmneziaWG 2.0 with `MTU = 1376` on both sides (working): ``` transfer: 18.90 MiB received, 4.77 MiB sent ``` Configuration difference: ``` # AmneziaWG 3.1 — broken $ grep -c '^MTU' /opt/amnezia/awg/awg0.conf 0 $ ip link show awg0 | grep -o 'mtu [0-9]*' mtu 1420 # AmneziaWG 2.0 — working $ grep '^MTU' /opt/amnezia/awg/awg0.conf MTU = 1376 ``` **Screenshots** N/A — the client UI shows a normal "Connected" state, the failure is invisible in the UI. **Desktop (please complete the following information):** - OS: Ubuntu 24.04.4 LTS (kernel 6.17) - Version: 5.0.1.5 **Smartphone (please complete the following information):** - Device: <fill in> - OS: <fill in> - Version: 5.0.1.5 **Server (please complete the following information):** - OS: Ubuntu 24.04, Docker, `amneziawg-go 0.0.20250522` **Additional context** Workaround — add `MTU = 1376` to the `[Interface]` section of the server's `awg0.conf` and apply it live: ``` docker exec <awg-container> ip link set awg0 mtu 1376 ``` After this the tunnel works normally on both desktop and mobile, with no other changes. Verified on a test bench: with an artificially limited path MTU the exact same symptom reproduces (small packets fine, large downloads fail), and lowering the tunnel MTU fixes it. Side note: the 3.1 installer also leaves `I1`–`I5` empty, while the 2.0 container had `I1` configured. The blog post announcing 3.1 explicitly states that protocol behaviour depends on `I1`, so an empty default looks unintended.
Author
Owner

@Damnboychi1 commented on GitHub (Aug 29, 2026):

#3064

<!-- gh-comment-id:5462371256 --> @Damnboychi1 commented on GitHub (Aug 29, 2026): #3064
Author
Owner

@HeisLuka commented on GitHub (Sep 9, 2026):

I checked this against 5.0.1.5, current dev, and the current amneziawg-tools awg-quick implementation.

There is a source-confirmed MTU lifecycle problem here, although I would describe it a little differently from the original report.

The server and client MTUs currently have completely separate ownership

The current AWG model already shows the asymmetry.

AwgClientConfig contains:

QString mtu;

but AwgServerConfig has no MTU field at all.

So there is currently no server-side AWG MTU value that can be:

generated
stored
displayed
exported
compared
or reconciled

with the client MTU.

The generated server config contains no MTU

Current client/server_scripts/awg/configure_container.sh creates:

[Interface]
PrivateKey = ...
Address = ...
ListenPort = ...
Jc = ...
...
DisableCookies = ...

but no:

MTU = ...

This is identical in 5.0.1.5 and current dev.

The container then starts the interface through:

awg-quick up /opt/amnezia/awg/awg0.conf

So the effective server MTU is delegated entirely to awg-quick.

awg-quick explains the observed 1420

When MTU is absent, current amneziawg-tools does approximately:

find MTU of endpoint/default route
        ↓
if none found, assume 1500
        ↓
interface MTU = underlay MTU - 80

The actual code ends with:

[[ $mtu -gt 0 ]] || mtu=1500
ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE"

Therefore on a normal Ethernet/default route with MTU 1500:

1500 - 80 = 1420

which exactly matches the server interface observed in this report.

So:

server awg0 MTU = 1420

is not random behavior.

It is the expected result of the current generated configuration.

But the Amnezia client uses a different MTU policy

Current client defaults are:

#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE)
    defaultMtu = 1280;
#else
    defaultMtu = 1376;
#endif

and AwgConfigModel::applyDefaultsToClientConfig() applies that value whenever the client config has no explicit MTU:

if (config.mtu.isEmpty()) {
    config.mtu = protocols::awg::defaultMtu;
}

That value is eventually passed to the platform tunnel interface as deviceMTU.

So a default self-hosted installation can intentionally become:

server:
    awg0 = 1420

desktop client:
    amn0/utun = 1376

mobile client:
    VPN interface = 1280

without any component treating this as inconsistent.

This is the source-confirmed bug

I would frame the invariant as:

AWG tunnel MTU must be an explicit part
of the deployed server/client configuration contract

rather than:

let awg-quick guess the server MTU
while Amnezia independently chooses the client MTU

This becomes more important for AWG 3.x because the transport adds behavior and overhead beyond plain WireGuard, while awg-quick's automatic calculation is still based on:

underlay MTU - 80

The installer has information about the protocol being deployed, but the server-side MTU calculation currently does not.

The runtime workaround is strong evidence

The reporter observed:

server awg0 = 1420
client tunnel = 1376

and changing only:

ip link set awg0 mtu 1376

made the connection usable.

That is a strong indication that the server's automatically selected MTU is part of the failure on this path.

I would, however, avoid describing the mechanism as:

every packet >1376 is necessarily dropped
by the receiving client interface

without a packet trace proving that exact drop point.

The safer conclusion is:

the server is allowed to generate tunnel traffic
according to a larger automatically-derived MTU
than the MTU policy Amnezia uses for the peer,
and lowering that server MTU removes the failure.

The packet loss may occur at the outer-path PMTU/fragmentation boundary rather than simply because the receiving interface has a smaller MTU.

This is not clearly a pure AWG 2.0 -> 3.1 installer regression

I also checked the older source corresponding to commit:

72ee32af1b4d2dbeb5811d0f76f46895e422d927

used around the 4.8.21 generation.

Its AWG server configuration script also does not contain an MTU directive.

So I cannot confirm from the repository that:

AWG 2.0 installer always wrote MTU = 1376

and AWG 3.1 later removed it.

If an older working container really contains:

MTU = 1376

it would be useful to determine where that line came from:

older client revision
older container image/tools
manual modification
migration/save operation

That historical question is separate from the current bug.

Current dev is already enough to establish:

server MTU is implicit
client MTU is explicit
and there is no reconciliation between them

The fix should not just hardcode 1376 into one shell script

There are several client defaults today:

desktop = 1376
mobile  = 1280

while a self-hosted server has one interface MTU shared by all peers.

So simply adding:

MTU = 1376

would still leave mobile peers using a different policy.

I think a better design is to introduce an explicit server-side MTU into the AWG configuration model.

For example:

AwgServerConfig.mtu

and generate:

[Interface]
MTU = <explicit server value>

Then generated client configurations should derive their maximum tunnel MTU from the same deployment policy.

A conservative invariant could be:

client effective MTU <= server/deployment-safe tunnel MTU

with a single safe AWG 3.x deployment value selected deliberately rather than inferred independently by awg-quick.

If 1280 is intended to be the universal conservative AWG 3.x value, that policy should be explicit in the installer/model instead of emerging only on mobile builds.

Diagnostics should expose the mismatch

The current UI can show client/server protocol settings, but because AwgServerConfig has no MTU field it cannot show:

configured server MTU
effective server MTU
client MTU

A useful diagnostics block would be:

AWG server config MTU:      1280
AWG server effective MTU:   1280
client configured MTU:      1280
client effective MTU:       1280
underlay MTU:               1500

That would make this class of failure much easier to diagnose.

Installer postcondition

After starting AWG, the installer can cheaply verify:

ip link show awg0

and record/check the effective MTU.

The installation should not silently accept:

expected AWG deployment MTU = X
effective interface MTU     = Y

when those values differ.

Regression matrix

  1. Fresh AWG 3.1 install on underlay MTU 1500.

Expected:

server MTU is explicitly controlled by Amnezia

not implicitly 1420 because awg-quick guessed it.

  1. Underlay MTU 1492 / PPPoE.

The server tunnel value should follow the AWG deployment policy and account for the real outer path.

  1. Desktop client.

Verify:

server effective MTU
client effective MTU

satisfy the deployment invariant.

  1. Android/iOS client.

Same check with the mobile MTU policy.

  1. Mixed peers:
desktop + Android + iOS

on one self-hosted AWG server.

The server setting must be safe for every generated client.

  1. Explicit custom MTU.

Change it in the supported configuration surface and verify it survives:

save
server reconfigure
container restart
client export
client import
reconnect
  1. AWG container restart.

awg-quick must not silently replace the intended MTU with its automatic underlay - 80 value.

  1. PMTU-sensitive transfer.

Test:

large HTTPS response
large UDP datagrams
IPv4
IPv6

and verify there is no black-hole behavior.

  1. Diagnostics.

A mismatched effective MTU must be visible rather than presenting only Connected.

So I would classify #3089 as confirmed on current dev, but with a slightly broader root cause:

Amnezia explicitly owns client AWG MTU while leaving server AWG MTU to awg-quick auto-detection. The server configuration model has no MTU at all, so the two sides cannot be kept in one deployment contract.

The reported 1420 server value is exactly what the current awg-quick auto-MTU algorithm produces on a 1500-byte underlay.

<!-- gh-comment-id:5609098899 --> @HeisLuka commented on GitHub (Sep 9, 2026): I checked this against `5.0.1.5`, current `dev`, and the current `amneziawg-tools` `awg-quick` implementation. There is a source-confirmed MTU lifecycle problem here, although I would describe it a little differently from the original report. ### The server and client MTUs currently have completely separate ownership The current AWG model already shows the asymmetry. `AwgClientConfig` contains: ```cpp QString mtu; ``` but `AwgServerConfig` has no MTU field at all. So there is currently no server-side AWG MTU value that can be: ```text generated stored displayed exported compared or reconciled ``` with the client MTU. ### The generated server config contains no `MTU` Current `client/server_scripts/awg/configure_container.sh` creates: ```ini [Interface] PrivateKey = ... Address = ... ListenPort = ... Jc = ... ... DisableCookies = ... ``` but no: ```ini MTU = ... ``` This is identical in `5.0.1.5` and current `dev`. The container then starts the interface through: ```sh awg-quick up /opt/amnezia/awg/awg0.conf ``` So the effective server MTU is delegated entirely to `awg-quick`. ### `awg-quick` explains the observed 1420 When `MTU` is absent, current `amneziawg-tools` does approximately: ```text find MTU of endpoint/default route ↓ if none found, assume 1500 ↓ interface MTU = underlay MTU - 80 ``` The actual code ends with: ```sh [[ $mtu -gt 0 ]] || mtu=1500 ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE" ``` Therefore on a normal Ethernet/default route with MTU 1500: ```text 1500 - 80 = 1420 ``` which exactly matches the server interface observed in this report. So: ```text server awg0 MTU = 1420 ``` is not random behavior. It is the expected result of the current generated configuration. ### But the Amnezia client uses a different MTU policy Current client defaults are: ```cpp #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(MACOS_NE) defaultMtu = 1280; #else defaultMtu = 1376; #endif ``` and `AwgConfigModel::applyDefaultsToClientConfig()` applies that value whenever the client config has no explicit MTU: ```cpp if (config.mtu.isEmpty()) { config.mtu = protocols::awg::defaultMtu; } ``` That value is eventually passed to the platform tunnel interface as `deviceMTU`. So a default self-hosted installation can intentionally become: ```text server: awg0 = 1420 desktop client: amn0/utun = 1376 mobile client: VPN interface = 1280 ``` without any component treating this as inconsistent. ### This is the source-confirmed bug I would frame the invariant as: ```text AWG tunnel MTU must be an explicit part of the deployed server/client configuration contract ``` rather than: ```text let awg-quick guess the server MTU while Amnezia independently chooses the client MTU ``` This becomes more important for AWG 3.x because the transport adds behavior and overhead beyond plain WireGuard, while `awg-quick`'s automatic calculation is still based on: ```text underlay MTU - 80 ``` The installer has information about the protocol being deployed, but the server-side MTU calculation currently does not. ### The runtime workaround is strong evidence The reporter observed: ```text server awg0 = 1420 client tunnel = 1376 ``` and changing only: ```sh ip link set awg0 mtu 1376 ``` made the connection usable. That is a strong indication that the server's automatically selected MTU is part of the failure on this path. I would, however, avoid describing the mechanism as: ```text every packet >1376 is necessarily dropped by the receiving client interface ``` without a packet trace proving that exact drop point. The safer conclusion is: ```text the server is allowed to generate tunnel traffic according to a larger automatically-derived MTU than the MTU policy Amnezia uses for the peer, and lowering that server MTU removes the failure. ``` The packet loss may occur at the outer-path PMTU/fragmentation boundary rather than simply because the receiving interface has a smaller MTU. ### This is not clearly a pure AWG 2.0 -> 3.1 installer regression I also checked the older source corresponding to commit: ```text 72ee32af1b4d2dbeb5811d0f76f46895e422d927 ``` used around the 4.8.21 generation. Its AWG server configuration script also does **not** contain an `MTU` directive. So I cannot confirm from the repository that: ```text AWG 2.0 installer always wrote MTU = 1376 ``` and AWG 3.1 later removed it. If an older working container really contains: ```ini MTU = 1376 ``` it would be useful to determine where that line came from: ```text older client revision older container image/tools manual modification migration/save operation ``` That historical question is separate from the current bug. Current `dev` is already enough to establish: ```text server MTU is implicit client MTU is explicit and there is no reconciliation between them ``` ### The fix should not just hardcode `1376` into one shell script There are several client defaults today: ```text desktop = 1376 mobile = 1280 ``` while a self-hosted server has one interface MTU shared by all peers. So simply adding: ```ini MTU = 1376 ``` would still leave mobile peers using a different policy. I think a better design is to introduce an explicit server-side MTU into the AWG configuration model. For example: ```text AwgServerConfig.mtu ``` and generate: ```ini [Interface] MTU = <explicit server value> ``` Then generated client configurations should derive their maximum tunnel MTU from the same deployment policy. A conservative invariant could be: ```text client effective MTU <= server/deployment-safe tunnel MTU ``` with a single safe AWG 3.x deployment value selected deliberately rather than inferred independently by `awg-quick`. If `1280` is intended to be the universal conservative AWG 3.x value, that policy should be explicit in the installer/model instead of emerging only on mobile builds. ### Diagnostics should expose the mismatch The current UI can show client/server protocol settings, but because `AwgServerConfig` has no MTU field it cannot show: ```text configured server MTU effective server MTU client MTU ``` A useful diagnostics block would be: ```text AWG server config MTU: 1280 AWG server effective MTU: 1280 client configured MTU: 1280 client effective MTU: 1280 underlay MTU: 1500 ``` That would make this class of failure much easier to diagnose. ### Installer postcondition After starting AWG, the installer can cheaply verify: ```sh ip link show awg0 ``` and record/check the effective MTU. The installation should not silently accept: ```text expected AWG deployment MTU = X effective interface MTU = Y ``` when those values differ. ### Regression matrix 1. Fresh AWG 3.1 install on underlay MTU 1500. Expected: ```text server MTU is explicitly controlled by Amnezia ``` not implicitly 1420 because `awg-quick` guessed it. 2. Underlay MTU 1492 / PPPoE. The server tunnel value should follow the AWG deployment policy and account for the real outer path. 3. Desktop client. Verify: ```text server effective MTU client effective MTU ``` satisfy the deployment invariant. 4. Android/iOS client. Same check with the mobile MTU policy. 5. Mixed peers: ```text desktop + Android + iOS ``` on one self-hosted AWG server. The server setting must be safe for every generated client. 6. Explicit custom MTU. Change it in the supported configuration surface and verify it survives: ```text save server reconfigure container restart client export client import reconnect ``` 7. AWG container restart. `awg-quick` must not silently replace the intended MTU with its automatic `underlay - 80` value. 8. PMTU-sensitive transfer. Test: ```text large HTTPS response large UDP datagrams IPv4 IPv6 ``` and verify there is no black-hole behavior. 9. Diagnostics. A mismatched effective MTU must be visible rather than presenting only `Connected`. So I would classify #3089 as **confirmed on current dev**, but with a slightly broader root cause: **Amnezia explicitly owns client AWG MTU while leaving server AWG MTU to `awg-quick` auto-detection. The server configuration model has no MTU at all, so the two sides cannot be kept in one deployment contract.** The reported `1420` server value is exactly what the current `awg-quick` auto-MTU algorithm produces on a 1500-byte underlay.
Author
Owner

@sp198469-hue commented on GitHub (Oct 12, 2026):

Всем привет, может быть кому то из русскоязычного человечества поможет, обновил Амнезию с 4 версии до пятой, дальше протокол Amnezia WG был сначала legacy потом 2.0, потом вышла 3.1 - дай думаю обнловлю. Пришлось снести пользователей (т.е. я пошел по пути "удалить протокол" -> "поставить протокол"), думал так будет чище, вернее смелее. Проблема №1 - после обновления протокола старая версия на Apple iPhone "не понимала" новый протокол, проблема №2 основная - ВПН начал дико тормозить. Решение для русскоговорящих тут https://github.com/amnezia-vpn/amnezia-client/issues/3274

<!-- gh-comment-id:6118606116 --> @sp198469-hue commented on GitHub (Oct 12, 2026): Всем привет, может быть кому то из русскоязычного человечества поможет, обновил Амнезию с 4 версии до пятой, дальше протокол Amnezia WG был сначала legacy потом 2.0, потом вышла 3.1 - дай думаю обнловлю. Пришлось снести пользователей (т.е. я пошел по пути "удалить протокол" -> "поставить протокол"), думал так будет чище, вернее смелее. Проблема №1 - после обновления протокола старая версия на Apple iPhone "не понимала" новый протокол, проблема №2 основная - ВПН начал дико тормозить. Решение для русскоговорящих тут https://github.com/amnezia-vpn/amnezia-client/issues/3274
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#1430
No description provided.