Skip to content

fix: [#222] Configure SSH port via cloud-init with reboot pattern#223

Merged
josecelano merged 3 commits intomainfrom
222-configure-ssh-service-port
Dec 12, 2025
Merged

fix: [#222] Configure SSH port via cloud-init with reboot pattern#223
josecelano merged 3 commits intomainfrom
222-configure-ssh-service-port

Conversation

@josecelano
Copy link
Copy Markdown
Member

Overview

Implements custom SSH port configuration during VM provisioning using cloud-init's reboot pattern following Hetzner best practices.

Fixes #222

Solution Overview

  • Cloud-init writes SSH config file and triggers system reboot
  • Reboot ensures clean SSH restart with new port configuration
  • Provision handler waits for configured port (not default port 22)
  • Increased timeout from 60s to 120s for cloud-init + reboot time

Key Changes

  1. Cloud-init template: Added write_files + runcmd with reboot
  2. Provision handler: Use configured SSH port in wait_for_readiness()
  3. SSH adapter: Increased DEFAULT_MAX_RETRY_ATTEMPTS from 30 to 60
  4. Documentation: Created ADR and updated issue spec

Technical Details

  • Cloud-init creates /etc/ssh/sshd_config.d/99-custom-port.conf
  • System reboot guarantees SSH only on custom port (no port 22)
  • Total timeout: 120 seconds (60 attempts × 2 second interval)
  • Testing confirmed: SSH listens only on configured port after reboot

Why Reboot Approach?

  • systemctl restart doesn't kill old SSH process when port changes
  • bootcmd ineffective - systemd auto-restarts SSH after bootcmd
  • Reboot is cleaner and follows Hetzner cloud-config tutorial

Files Modified

  • templates/tofu/common/cloud-init.yml.tera
  • src/application/command_handlers/provision/handler.rs
  • src/adapters/ssh/config.rs
  • docs/decisions/cloud-init-ssh-port-reboot.md (new)
  • docs/decisions/README.md
  • docs/issues/222-configure-ssh-service-port.md
  • project-words.txt

References

- Add ssh_port field to CloudInitContext with builder pattern
- Update cloud-init.yml.tera to configure SSH port via write_files
- Pass ssh_port from environment to TofuProjectGenerator
- Refactor CloudInitRenderer: remove unused provider field, move ssh_port to render method
- Update all tests to use new CloudInitRenderer API
- Update issue spec to document cloud-init approach and explain why Ansible was discarded

This implementation configures SSH port during VM initialization (provision phase)
rather than post-provisioning (configure phase), ensuring SSH service is listening
on the configured port before any Ansible connections are attempted.
Implements custom SSH port configuration during VM provisioning using
cloud-init's reboot pattern following Hetzner best practices.

Solution Overview:
- Cloud-init writes SSH config file and triggers system reboot
- Reboot ensures clean SSH restart with new port configuration
- Provision handler waits for configured port (not default port 22)
- Increased timeout from 60s to 120s for cloud-init + reboot time

Key Changes:
1. Cloud-init template: Added write_files + runcmd with reboot
2. Provision handler: Use configured SSH port in wait_for_readiness()
3. SSH adapter: Increased DEFAULT_MAX_RETRY_ATTEMPTS from 30 to 60
4. Documentation: Created ADR and updated issue spec

Technical Details:
- Cloud-init creates /etc/ssh/sshd_config.d/99-custom-port.conf
- System reboot guarantees SSH only on custom port (no port 22)
- Total timeout: 120 seconds (60 attempts × 2 second interval)
- Testing confirmed: SSH listens only on configured port after reboot

Why Reboot Approach:
- systemctl restart doesn't kill old SSH process when port changes
- bootcmd ineffective - systemd auto-restarts SSH after bootcmd
- Reboot is cleaner and follows Hetzner cloud-config tutorial

Files Modified:
- templates/tofu/common/cloud-init.yml.tera
- src/application/command_handlers/provision/handler.rs
- src/adapters/ssh/config.rs
- docs/decisions/cloud-init-ssh-port-reboot.md (new)
- docs/decisions/README.md
- docs/issues/222-configure-ssh-service-port.md
- project-words.txt

References:
- Hetzner cloud-config tutorial section 5.3
- Issue #222
The cloud-init template was unconditionally configuring SSH port and
rebooting the VM, even when using the default port 22. This caused:
- Unnecessary VM reboots for environments using default SSH port
- E2E infrastructure lifecycle test failures on GitHub Actions
- Longer provisioning times for default configurations

Root Cause:
- Cloud-init template always wrote SSH port config file and triggered reboot
- E2E test uses default port 22, but was forced to reboot unnecessarily
- GitHub runner timed out waiting for SSH after unnecessary reboot

Solution:
- Add Tera conditional {% if ssh_port != 22 %} around write_files and runcmd
- SSH port configuration and reboot now only happen for custom ports
- E2E tests with default port 22 no longer trigger unnecessary reboots
- Provisioning is faster for default port configurations

Benefits:
- Faster provisioning when using default SSH port (no reboot overhead)
- E2E tests pass on GitHub Actions without timeout issues
- Still maintains reboot pattern for custom ports (proper SSH restart)
- Conditional approach is more efficient and user-friendly

Files Modified:
- templates/tofu/common/cloud-init.yml.tera: Add conditional around write_files and runcmd
- docs/issues/222-configure-ssh-service-port.md: Document conditional behavior
- docs/decisions/cloud-init-ssh-port-reboot.md: Add positive consequence about conditional execution

Technical Details:
The Tera template now checks if ssh_port != 22 before:
1. Writing /etc/ssh/sshd_config.d/99-custom-port.conf
2. Executing runcmd: [reboot]

This preserves the reboot pattern for custom ports (ensuring clean SSH restart)
while avoiding unnecessary reboots for default port configurations.

Testing:
- All 1424 unit tests pass
- All doctests pass
- E2E infrastructure lifecycle tests pass (default port 22, no reboot)
- E2E deployment workflow tests pass
- Documentation builds successfully
- Pre-commit verification: ✅ All checks passed
@josecelano
Copy link
Copy Markdown
Member Author

ACK 41cda98

@josecelano josecelano merged commit fd63be3 into main Dec 12, 2025
34 checks passed
josecelano added a commit that referenced this pull request Dec 12, 2025
Removed docs/issues/222-configure-ssh-service-port.md as PR #223 has been
successfully merged into main. The implementation details are now preserved
in the ADR (docs/decisions/cloud-init-ssh-port-reboot.md) and git history.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configure SSH Service Port During Infrastructure Configuration

1 participant