CHR: Cloud-Init and First-Boot Configuration
CHR: Cloud-Init and First-Boot Configuration
Section titled “CHR: Cloud-Init and First-Boot Configuration”RouterOS CHR supports automated first-boot configuration on cloud platforms through each provider’s metadata service. When CHR starts for the first time, it reads configuration data passed via the cloud provider’s user-data or startup-script mechanism and executes it as a RouterOS script.
This allows automated provisioning without manual console access: setting passwords, assigning IP addresses, configuring firewall rules, and more.
How CHR Processes User Data
Section titled “How CHR Processes User Data”CHR reads user-data from the cloud platform’s metadata service (typically at 169.254.169.254) during first boot. The content is treated as a RouterOS CLI script and executed once.
Key behavior:
- Executed once on first boot only
- Content is interpreted as RouterOS CLI commands, not standard cloud-init YAML
- Script execution errors appear in
/log print - No output is written back to the cloud platform — verify results by connecting to the router
CHR does not support standard cloud-init YAML format (e.g., #cloud-config with users:, packages:, runcmd: keys). Use RouterOS CLI syntax only.
User Data Format
Section titled “User Data Format”User data must be plain RouterOS CLI commands, one per line:
/user set admin password=SecurePassword123!/ip service disable telnet,ftp,www/system identity set name=my-routerMulti-line commands using RouterOS braces are supported:
/ip firewall filteradd chain=input action=accept connection-state=established,related comment="Accept established"add chain=input action=accept protocol=tcp dst-port=22,8291 src-address=10.0.0.0/8 comment="Management"add chain=input action=drop comment="Drop all other input"Configuring User Data by Platform
Section titled “Configuring User Data by Platform”AWS EC2
Section titled “AWS EC2”Pass user data in the EC2 launch wizard under Advanced Details → User data, or via the CLI:
aws ec2 run-instances \ --image-id ami-xxxxx \ --instance-type t3.small \ --user-data file://chr-init.rsc \ --key-name my-key \ --security-group-ids sg-xxxxx \ --subnet-id subnet-xxxxxGoogle Cloud Platform
Section titled “Google Cloud Platform”Use the startup-script metadata key:
gcloud compute instances create chr-router \ --image mikrotik-chr-7x \ --machine-type e2-small \ --can-ip-forward \ --metadata-from-file startup-script=chr-init.rsc \ --zone us-central1-aMicrosoft Azure
Section titled “Microsoft Azure”Azure passes custom data to the VM via the --custom-data parameter:
az vm create \ --resource-group chr-resources \ --name chr-router \ --attach-os-disk chr-os-disk \ --os-type Linux \ --size Standard_B2s \ --nics chr-nic \ --custom-data chr-init.rscVultr / Hetzner / Generic Providers
Section titled “Vultr / Hetzner / Generic Providers”Most cloud providers that support user-data pass it via the standard cloud metadata endpoint at http://169.254.169.254/latest/user-data or provider-specific equivalents. CHR reads from this endpoint automatically on first boot.
Example Configuration Scripts
Section titled “Example Configuration Scripts”:::note Interface Mapping in Cloud Deployments In cloud CHR deployments (AWS, GCP, Azure, etc.), ether1 is typically the management interface, not WAN. Cloud providers attach interfaces in the order they are defined in the VM configuration, and the first interface is usually your management or primary network interface.
The examples below use ether1 as WAN for illustration. Before deploying, check your cloud provider’s documentation or console to confirm which interface corresponds to which network. Using the wrong interface in your startup script can result in loss of management access.
:::
Basic Hardening
Section titled “Basic Hardening”# chr-hardening.rsc — minimum security configuration/user set admin password=StrongPassword123!/ip service disable telnet,ftp,www,api,api-ssl/ip service set ssh port=22 address=10.0.0.0/8/ip service set winbox port=8291 address=10.0.0.0/8/tool bandwidth-server set enabled=no/ip neighbor discovery-settings set discover-interface-list=none/system identity set name=chr-prod-01IP Address and Routing
Section titled “IP Address and Routing”# chr-network.rsc — initial IP configuration/ip address add address=10.10.1.1/24 interface=ether1 comment="WAN"/ip address add address=192.168.100.1/24 interface=ether2 comment="LAN"/ip route add dst-address=0.0.0.0/0 gateway=10.10.1.254 comment="Default route"/ip dns set servers=1.1.1.1,8.8.8.8 allow-remote-requests=noFirewall Rules
Section titled “Firewall Rules”# chr-firewall.rsc — basic input protection/ip firewall filteradd chain=input action=accept connection-state=established,related comment="Accept established/related"add chain=input action=accept connection-state=untracked comment="Accept untracked"add chain=input action=drop connection-state=invalid comment="Drop invalid"add chain=input action=accept protocol=icmp comment="Accept ICMP"add chain=input action=accept protocol=tcp dst-port=22,8291 src-address=10.0.0.0/8 comment="Management"add chain=input action=drop comment="Drop all other input"
/ip firewall filteradd chain=forward action=accept connection-state=established,related comment="Accept established/related"add chain=forward action=drop connection-state=invalid comment="Drop invalid"add chain=forward action=accept out-interface=ether1 comment="Allow LAN to WAN"NAT Gateway Configuration
Section titled “NAT Gateway Configuration”# chr-nat.rsc — source NAT for LAN-to-WAN traffic/ip firewall natadd chain=srcnat out-interface=ether1 action=masquerade comment="Masquerade LAN traffic"SSH Key Authentication
Section titled “SSH Key Authentication”# chr-ssh-keys.rsc — add SSH public key for admin user/user ssh-keys import public-key-file=sftp://admin@192.168.88.1/id_rsa.pub user=adminAlternatively, pre-stage the key using RouterOS script syntax:
/file add name=admin.pub contents="ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB... user@host"/user ssh-keys import public-key-file=admin.pub user=adminLicense Activation
Section titled “License Activation”# chr-license.rsc — activate CHR license (run after first boot)/system license generate-new-id/system/license renew account=your-mikrotik-account password=your-password level=p1Run /system license generate-new-id before requesting a license if your instance was cloned or launched from a shared AMI/image. Multiple instances from the same image may share a system ID.
Complete First-Boot Script Example
Section titled “Complete First-Boot Script Example”# Complete first-boot initialization for cloud CHR deployment
# 1. Identity/system identity set name=cloud-chr-01
# 2. Security — change default password immediately/user set admin password=SecurePassword123!
# 3. Disable unused management services/ip service disable telnet,ftp,www,api,api-ssl/ip service set ssh address=10.0.0.0/8/ip service set winbox address=10.0.0.0/8
# 4. Disable bandwidth server and neighbor discovery/tool bandwidth-server set enabled=no/ip neighbor discovery-settings set discover-interface-list=none
# 5. Network configuration/ip address add address=10.10.1.1/24 interface=ether1 comment="WAN"/ip address add address=192.168.100.1/24 interface=ether2 comment="LAN"/ip route add dst-address=0.0.0.0/0 gateway=10.10.1.254
# 6. DNS/ip dns set servers=1.1.1.1,8.8.8.8
# 7. Firewall — input chain/ip firewall filteradd chain=input action=accept connection-state=established,relatedadd chain=input action=drop connection-state=invalidadd chain=input action=accept protocol=icmpadd chain=input action=accept protocol=tcp dst-port=22,8291 src-address=10.0.0.0/8add chain=input action=dropWarning — management lockout risk: The rule above only permits SSH and Winbox access from
10.0.0.0/8. If your management workstation has a public IP address (common in cloud environments), thedrop allrule will block your access immediately after the script runs. Before executing this script, replacesrc-address=10.0.0.0/8with your actual management IP or subnet — for example,src-address=203.0.113.10/32. If you need to permit access on a specific interface regardless of source IP, addin-interface=ether1to the accept rule instead of (or in addition to) thesrc-addressrestriction.
# 8. Firewall — forward chain/ip firewall filteradd chain=forward action=accept connection-state=established,relatedadd chain=forward action=drop connection-state=invalid
# 9. NAT/ip firewall nat add chain=srcnat out-interface=ether1 action=masquerade
# 10. Generate new system ID (important when launching from a shared image)/system license generate-new-id
# 11. Log completion/log info "CHR first-boot initialization complete"Verifying Script Execution
Section titled “Verifying Script Execution”After boot, verify the script ran correctly:
# Check system log for script output and errors/log print where topics~"script"
# Verify identity was set/system identity print
# Verify interfaces and IPs/ip address print
# Check firewall rules applied/ip firewall filter printTroubleshooting
Section titled “Troubleshooting”Script Not Applied
Section titled “Script Not Applied”- Verify the script was passed correctly (check cloud provider console for user-data)
- Check
/log printfor any error messages - User data executes only on first boot — rebooting will not re-execute it
- Ensure the script uses RouterOS CLI syntax, not bash or YAML
Locked Out After Firewall Rules Applied
Section titled “Locked Out After Firewall Rules Applied”If the first-boot script included a drop all input rule and you can no longer reach the router, the
management source address did not match the permitted subnet. To recover:
- Use the cloud provider’s serial console or out-of-band access (AWS EC2 Instance Connect, GCP Serial Port, Azure Serial Console) — these bypass firewall rules.
- Once connected, remove or relax the offending rule:
# Identify the rule number/ip firewall filter print
# Remove the drop rule (adjust number to match your output)/ip firewall filter remove [find chain=input action=drop]
# Re-add with your actual management IP/ip firewall filter add chain=input action=accept protocol=tcp dst-port=22,8291 src-address=<YOUR-IP>/32/ip firewall filter add chain=input action=drop- To prevent recurrence, update the
src-addressin your cloud-init script before the next deployment.
Commands Partially Applied
Section titled “Commands Partially Applied”RouterOS scripts execute line-by-line. If a command fails, execution continues with the next line. Check the log for specific errors:
/log print where message~"error"Password Not Changed
Section titled “Password Not Changed”If the password script ran but the password did not change, verify there are no quoting issues in the user-data string. Test the command manually in the CLI:
/user set admin password=TestPassword123