Skip to content

Make always use DERP option configurable#592

Merged
frenck merged 2 commits into
hassio-addons:mainfrom
lmagyar:pr-always-use-derp
Feb 5, 2026
Merged

Make always use DERP option configurable#592
frenck merged 2 commits into
hassio-addons:mainfrom
lmagyar:pr-always-use-derp

Conversation

@lmagyar

@lmagyar lmagyar commented Dec 2, 2025

Copy link
Copy Markdown
Collaborator

Proposed Changes

Handles a maddening issue, when connections to Home Assistant are constantly dropping, while the device is always ping-able.

The root cause can be ISP routing misconfiguration (dropping certain UDP packets).

My hunch is that this issue is more regular, but nobody figures out the root cause. This made me mad when I experienced, but the working ping missled me to think "it's a HA web server issue". Until investigating #569 finally the root cause is identified.

Related Issues

fixes #569

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced always_use_derp configuration option to force peer communication over DERP by disabling UDP. Disabled by default.
  • Documentation

    • Added documentation and translations for the new configuration option, including guidance on when to enable it.
  • Changes

    • Modified userspace networking default behavior; now only activates when explicitly configured to true.

@coderabbitai

coderabbitai Bot commented Dec 2, 2025

Copy link
Copy Markdown

Walkthrough

A new always_use_derp configuration option has been introduced across documentation, configuration schema, runtime scripts, and translation files. This option forces all peer communication over DERP by disabling UDP when enabled. The feature is disabled by default and includes corresponding startup logic to set environment variables.

Changes

Cohort / File(s) Summary
Documentation & Localization
tailscale/DOCS.md, tailscale/translations/en.yaml
Added comprehensive documentation and English translation entries for the new always_use_derp option, including use cases and default behavior description.
Configuration Schema
tailscale/config.yaml
Added always_use_derp: false option with corresponding boolean schema definition.
Runtime Startup Script
tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
Modified userspace networking default behavior and added conditional logic to export TS_DEBUG_ALWAYS_USE_DERP environment variable when the option is enabled, with accompanying logging.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A DERP route appears, UDP takes a rest,
When troubles with packets just won't pass the test,
This toggle so gentle, disabled by default,
Helps troublesome connections find peace in the vault,
One option, one change, all problems unwrap!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: introducing a configurable option for the always_use_derp feature across documentation, configuration, and initialization scripts.
Linked Issues check ✅ Passed The PR implements a configurable always_use_derp option to address UDP connectivity issues reported in #569, providing a mitigation for ISP routing misconfiguration affecting Home Assistant web access.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the always_use_derp feature: documentation, configuration schema, startup script, and translations; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lmagyar lmagyar added the new-feature New features or options. label Dec 2, 2025
@lmagyar lmagyar changed the title Make always use derp option configurable Make always use DERP option configurable Dec 2, 2025
@lmagyar lmagyar added the no-stale This issue or PR is exempted from the stable bot. label Dec 25, 2025
@lmagyar

lmagyar commented Jan 5, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 5, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @tailscale/translations/en.yaml:
- Around line 37-42: Correct the typo in the translation entry for the
always_use_derp option: change the value of the name field from "Alwas use DERP
connection" to "Always use DERP connection" so the label reads correctly; update
only the name string under the always_use_derp key (leave description
unchanged).
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6535433 and 03bab2f.

📒 Files selected for processing (8)
  • tailscale/DOCS.md
  • tailscale/config.yaml
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/share-homeassistant/run
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
  • tailscale/rootfs/usr/bin/subnet-routes
  • tailscale/translations/en.yaml
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 541
File: tailscale/config.yaml:30-37
Timestamp: 2025-09-16T23:47:20.987Z
Learning: In the hassio-addons/addon-tailscale project, the advertise_routes configuration uses `local_subnets` (with underscore) as the default value, and the runtime regex patterns use `local.subnets` (with dot) which correctly matches the underscore variant because '.' in regex matches any single character including '_'.
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 509
File: tailscale/config.yaml:0-0
Timestamp: 2025-09-17T23:53:19.467Z
Learning: In the Tailscale add-on config.yaml, there is an intentional naming mismatch between map entries and schema field names: map entries use actual Home Assistant directory names (like `homeassistant_config`, `all_addon_configs`) while schema fields use user-friendly names (like `config`, `addon_configs`). This allows correct directory mapping while presenting cleaner option names in the UI.
📚 Learning: 2024-11-15T17:38:56.576Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 419
File: tailscale/rootfs/command/with-contenv-merge:8-12
Timestamp: 2024-11-15T17:38:56.576Z
Learning: The script `with-contenv-merge` in `tailscale/rootfs/command/` is directly copied from s6 and should not be modified.

Applied to files:

  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/share-homeassistant/run
  • tailscale/rootfs/usr/bin/subnet-routes
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
📚 Learning: 2025-11-06T10:55:55.062Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 566
File: tailscale/rootfs/usr/bin/subnet-routes:1-2
Timestamp: 2025-11-06T10:55:55.062Z
Learning: In the hassio-addons/addon-tailscale project, utility scripts in `/usr/bin/` (like `subnet-routes`, `protect-subnet-routes`, `unprotect-subnet-routes`) are invoked from s6-overlay service scripts or NetworkManager dispatchers that already use `#!/command/with-contenv bashio`. These utilities inherit the environment from their parent processes and therefore use `#!/usr/bin/env bashio` rather than `#!/command/with-contenv bashio`.

Applied to files:

  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/share-homeassistant/run
  • tailscale/rootfs/usr/bin/subnet-routes
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
📚 Learning: 2024-10-09T10:32:14.110Z
Learnt from: mikz
Repo: hassio-addons/addon-tailscale PR: 407
File: tailscale/rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run:24-29
Timestamp: 2024-10-09T10:32:14.110Z
Learning: In the Tailscale add-on for Home Assistant, the `tailscale/rootfs` directory is copied into the container's root directory via the Dockerfile, ensuring that files like `/etc/nginx/templates/homeassistant.gtpl` are present at runtime.

Applied to files:

  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/share-homeassistant/run
  • tailscale/DOCS.md
📚 Learning: 2025-09-17T23:53:19.467Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 509
File: tailscale/config.yaml:0-0
Timestamp: 2025-09-17T23:53:19.467Z
Learning: In the Tailscale add-on config.yaml, there is an intentional naming mismatch between map entries and schema field names: map entries use actual Home Assistant directory names (like `homeassistant_config`, `all_addon_configs`) while schema fields use user-friendly names (like `config`, `addon_configs`). This allows correct directory mapping while presenting cleaner option names in the UI.

Applied to files:

  • tailscale/config.yaml
  • tailscale/DOCS.md
📚 Learning: 2025-11-23T00:06:06.013Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 541
File: tailscale/config.yaml:29-30
Timestamp: 2025-11-23T00:06:06.013Z
Learning: In the Home Assistant Tailscale add-on (hassio-addons/addon-tailscale), userspace_networking defaults to true because TUN networking can cause problems and is not needed for basic Home Assistant accessibility. This is an intentional deviation from stock Tailscale's default kernel/TUN networking.

Applied to files:

  • tailscale/config.yaml
  • tailscale/translations/en.yaml
  • tailscale/DOCS.md
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run
  • tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
📚 Learning: 2025-09-16T23:47:20.987Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 541
File: tailscale/config.yaml:30-37
Timestamp: 2025-09-16T23:47:20.987Z
Learning: In the hassio-addons/addon-tailscale project, the advertise_routes configuration uses `local_subnets` (with underscore) as the default value, and the runtime regex patterns use `local.subnets` (with dot) which correctly matches the underscore variant because '.' in regex matches any single character including '_'.

Applied to files:

  • tailscale/rootfs/usr/bin/subnet-routes
  • tailscale/DOCS.md
📚 Learning: 2025-10-29T20:47:02.818Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 562
File: tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run:120-128
Timestamp: 2025-10-29T20:47:02.818Z
Learning: In the hassio-addons/addon-tailscale project, when checking for IPv6 addresses in bash scripts, use the regex format `if [[ "${variable}" =~ .*:.* ]]; then` for consistency with other IPv6 tests in the codebase.

Applied to files:

  • tailscale/rootfs/usr/bin/subnet-routes
  • tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
📚 Learning: 2025-07-28T00:02:02.296Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 445
File: tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh:24-38
Timestamp: 2025-07-28T00:02:02.296Z
Learning: In Home Assistant add-ons, mandatory configuration options are automatically filled with default values by the supervisor, so there's no need to guard against overwriting user-configured values during migration since the option will always have a value.

Applied to files:

  • tailscale/DOCS.md
📚 Learning: 2025-06-11T20:37:01.152Z
Learnt from: lmagyar
Repo: hassio-addons/addon-tailscale PR: 455
File: tailscale/rootfs/usr/bin/magicdns-ingress-proxy-forwarding:100-106
Timestamp: 2025-06-11T20:37:01.152Z
Learning: In the Home Assistant Tailscale add-on, DNATing DNS packets to port 0 (e.g. `--to-destination 127.0.0.1:0`) is an intentional technique to drop traffic. iptables accepts port 0, rewrites the destination port to 0, and the packet is rejected by the stack, effectively discarding it. This is valid and should not be flagged as an error in future reviews.

Applied to files:

  • tailscale/DOCS.md
🔇 Additional comments (33)
tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run (2)

26-29: LGTM - More explicit configuration check.

The change from implicit default handling to an explicit true check improves clarity and aligns with the PR's goal of making configuration options explicit.


31-35: LGTM - Correct implementation of always_use_derp feature.

The implementation correctly:

  • Checks the configuration option
  • Exports the appropriate Tailscale environment variable TS_DEBUG_ALWAYS_USE_DERP
  • Notifies the user about the routing behavior change
tailscale/translations/en.yaml (1)

8-110: LGTM - Improved documentation clarity.

The addition of explicit default state descriptions ("This option is enabled/disabled by default") across all configuration options improves user understanding. The updated advertise_routes description correctly documents the local_subnets functionality.

tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh (5)

61-66: LGTM - More explicit configuration check.

The change to explicitly check for userspace_networking being true improves code clarity and consistency across the codebase.


68-72: LGTM - Improved precision for local_subnets handling.

The change from checking mere existence of advertise_routes to checking for the specific local_subnets value is more precise and aligns with the new sentinel-based approach for route handling.


74-77: LGTM - Consistent with explicit configuration pattern.


79-82: LGTM - Consistent with explicit configuration pattern.


89-92: LGTM - More precise service disabling logic.

The change to check for an explicit 'disabled' value rather than absence ensures the service is only disabled when intentionally configured to be disabled.

tailscale/rootfs/usr/bin/subnet-routes (3)

27-48: LGTM - Correct implementation of local_subnets sentinel handling.

The logic correctly:

  • Detects the local_subnets sentinel value
  • Recursively calls subnet-routes local to collect actual subnet routes
  • Warns users if no local subnets are found
  • Falls back to using configured addresses otherwise

The recursive call is safe (no risk of infinite recursion) and the exact pattern match ^local_subnets$ is appropriate.


50-70: LGTM - Proper filtering of link-local addresses and forwarding checks.

The implementation correctly:

  • Filters out IPv6 link-local addresses (fe80::/10)
  • Filters out IPv4 link-local addresses (169.254.0.0/16)
  • Verifies IP forwarding is enabled for the appropriate address family before advertising routes
  • Uses the consistent IPv6 detection pattern .*:.* as per codebase conventions

72-77: LGTM - Clear deduplication and caching logic.

The comment update accurately describes the deduplication process, and the implementation correctly uses sort -u to remove duplicates before caching.

tailscale/rootfs/etc/s6-overlay/s6-rc.d/share-homeassistant/run (1)

72-72: The share_on_port configuration already has a default value defined in config.yaml (line 39: share_on_port: 443). The removal of the inline default fallback is safe because the configuration system provides the default value automatically. No action is needed.

Likely an incorrect or invalid review comment.

tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run (12)

19-24: LGTM! Explicit boolean handling is correct.

The refactoring from implicit defaults to explicit true/false checks for accept_dns improves clarity and ensures consistent behavior regardless of how bashio handles config defaults.


26-31: LGTM! Consistent explicit boolean pattern.

The accept_routes handling follows the same clear pattern as accept_dns, ensuring predictable behavior.


34-38: LGTM! Correct mutual exclusivity check.

The validation correctly prevents advertising as an exit node while simultaneously using another exit node, which would be a configuration error.


40-45: LGTM! Consistent pattern for exit node advertisement.

The explicit boolean handling for advertise_exit_node is correct and consistent.


47-55: LGTM! Explicit exit node configuration is correct.

The logic properly handles both cases:

  • When an exit node is specified: configures it and enables LAN access (required for internal hassio/docker network access)
  • When not specified: explicitly clears the exit node and disables LAN access

This ensures deterministic behavior on every run.


57-62: LGTM! Correct app connector advertisement handling.

The explicit boolean pattern for advertise_connector is consistent with the refactoring approach.


65-66: LGTM! Unconditional login_server configuration is correct.

Since login_server now has a mandatory default value in config.yaml, it's correct to always retrieve and apply it without conditional checks.


68-73: LGTM! Explicit stateful filtering configuration.

The boolean handling for stateful_filtering follows the consistent pattern.


75-80: LGTM! Explicit SNAT configuration is correct.

The boolean handling for snat_subnet_routes maintains the consistent pattern throughout the file.


83-83: LGTM! Simplified tags retrieval.

The direct jq path expression for tags is cleaner and functionally equivalent.


147-148: LGTM! Correct condition for colliding routes check.

Checking for colliding routes only when accept_routes is true makes logical sense—if routes aren't being accepted, there's no possibility of collision.


170-170: LGTM! Consistent boolean check for userspace networking notice.

The explicit true check for userspace_networking aligns with the refactoring pattern.

tailscale/config.yaml (2)

28-44: LGTM! Default values align with documented behavior.

The options block correctly defines defaults that match the documentation:

  • Boolean options have appropriate true/false defaults
  • advertise_exit_node: true advertises the capability but still requires admin console approval to function
  • New always_use_derp: false option is disabled by default as documented
  • advertise_routes: [local_subnets] matches the documented default behavior

All defaults are consistent with the documentation in DOCS.md.


45-63: LGTM! Schema definitions are correct and properly constrained.

The schema block properly validates all configuration options:

  • Boolean types are non-nullable as intended
  • advertise_routes pattern correctly accepts local_subnets or valid CIDR notation (based on learnings, the regex dot matches underscore)
  • exit_node is correctly optional with comprehensive pattern matching
  • share_on_port is correctly restricted to the three ports allowed by Tailscale (443, 8443, 10000)
  • tags pattern enforces valid Tailscale tag format
  • All enum-style options use appropriate list() constraints
tailscale/DOCS.md (7)

43-64: LGTM! Clear configuration instructions.

The updated instructions correctly describe the workflow where the add-on advertises capabilities by default, which can then be enabled through the Tailscale admin console. The steps are clear and actionable.


66-88: LGTM! YAML example accurately reflects defaults.

The configuration example correctly shows all options with their default values, including the new always_use_derp: false option and local_subnets in the advertise_routes list.


101-101: LGTM! Consistent default statements.

The "This option is enabled by default" statements for accept_dns, accept_routes, advertise_exit_node, and advertise_connector all correctly match their true defaults in config.yaml.

Also applies to: 115-115, 126-126, 146-146


161-162: LGTM! Correct default behavior documented.

The documentation correctly states that advertise_routes includes local_subnets by default, matching the config.yaml default.


235-235: LGTM! All default statements are accurate.

The default statements throughout the documentation correctly reflect the values in config.yaml:

  • share_homeassistant: disabled by default ✓
  • share_on_port: 443 by default ✓
  • snat_subnet_routes: enabled by default ✓
  • stateful_filtering: disabled by default ✓
  • taildrop: enabled by default ✓
  • userspace_networking: enabled by default ✓

Also applies to: 311-311, 318-318, 337-337, 352-352, 362-362


490-490: LGTM! Correct link reference added.

The new [tailscale_machines] link correctly points to the Tailscale admin console machines page and is properly referenced earlier in the document at line 56.


164-177: The always_use_derp configuration is properly implemented in the tailscaled startup script.

The verification confirms that the documentation in DOCS.md is accurate and the implementation is complete:

  • The always_use_derp option is read from the configuration in tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run (lines 31-34)
  • When enabled, it correctly exports TS_DEBUG_ALWAYS_USE_DERP=true to the tailscaled process
  • Appropriate logging is in place to notify users when DERP routing is active
  • The default value (false) in config.yaml matches the documentation

The implementation properly integrates the configuration option throughout the addon (config schema, startup script, documentation, and translations).

Comment thread tailscale/translations/en.yaml
@lmagyar lmagyar force-pushed the pr-always-use-derp branch 4 times, most recently from 794ebe4 to d744df2 Compare February 3, 2026 21:06
@lmagyar lmagyar force-pushed the pr-always-use-derp branch from d744df2 to f5a96b2 Compare February 5, 2026 15:14
@lmagyar lmagyar marked this pull request as ready for review February 5, 2026 15:16

@frenck frenck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @lmagyar 👍

../Frenck

                       

Blogging my personal ramblings at frenck.dev

@frenck frenck merged commit 0c44472 into hassio-addons:main Feb 5, 2026
13 checks passed
@lmagyar lmagyar deleted the pr-always-use-derp branch February 5, 2026 15:25
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

new-feature New features or options. no-stale This issue or PR is exempted from the stable bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Very unreliable connection

3 participants