Spin provision under W11 + WSL2 + Ubuntu 24.04.1 LTS: Failed to connect to the host via ssh: Bad owner or permissions on /root/.ssh/config

Hello,

I encountered this difficulty when wanting to test out spin on an existing Laravel project using the Laravel Basic template. This machine is running Win11 with Ubuntu 24.04.1 LTS under WSL2. The intent was to deploy to an existing VPS I have, which has working ssh keypair access with my main user and also made sure root works for testing this out, ie ssh root@ip (or ssh root@domain). That all works from this WSL2 environment.

Docker is already installed and working on this machine as well as the VPS.

I installed Spin with Install to Linux. Added to path and confirmed with:

$ spin version
Spin Version:
v2.1.1 [stable] (User Installed)

I then did spin init laravel to add to my existing project. It gave me the following output:

grep: /home/user/dev/project/.env: No such file or directory
👉 "docker-compose.yml" already exists. Skipping...
✅ "docker-compose.prod.yml" has been created.
✅ "docker-compose.dev.yml" has been created.
👉 "Dockerfile" already exists. Skipping...
✅ ".infrastructure/conf/traefik/prod/traefik.yml" has been created.
✅ ".infrastructure/conf/traefik/dev/certificates/local-dev.pem" has been created.
✅ ".infrastructure/conf/traefik/dev/certificates/local-dev-key.pem" has been created.
✅ ".infrastructure/conf/traefik/dev/traefik-certs.yml" has been created.
✅ ".infrastructure/conf/traefik/dev/traefik.yml" has been created.
✅ ".infrastructure/volume_data/.gitignore" has been created.
✅ ".spin-inventory.ini" has been created.
✅ ".spin.yml" has been created.

So far that is all as expected, as I have my own Dockerfile and docker-compose.yaml set up. I don’t have a .env in this base dir, as the project itself is in ./app.

I chose not to encrypt Spin configurations for now. Entered email when prompted for Let’s Encrypt:

✅ Updated ".infrastructure/conf/traefik/prod/traefik.yml" with your email.
🚀 Your project is now ready for "spin up"!
👉 Learn how to use your template at https://github.com/serversideup/spin-template-laravel-basic
🚨 COMPLETED WITH WARNINGS:
👉 Some files already existed when copying the template, so we left those files alone.
👉 Check the output above to figure out what files you may need to update manually.

Then I opened ./spin-inventory.ini and added my production and staging domains:

####################
# Host Types
####################

[production_manager_servers]
myapp.com

[staging_manager_servers]
staging.myapp.com

####################
# Swarm Roles
####################
[swarm_managers:children]
production_manager_servers
staging_manager_servers

####################
# Environment
####################
[production:children]
production_manager_servers

[staging:children]
staging_manager_servers

[all_servers:children]
production
staging

then went in ./spin.yml and updated server_contact: with an email, left the rest alone until the users section. I specified a username, name, and pw hash from mkpasswd + an ssh pubkey that I am able to ssh into the server with. Example:

---
###########################################
# Basic Server Configuration
###########################################
server_timezone: "Etc/UTC"
server_contact: [email protected]

# If you the SSH port below, you may need to run `spin provision -p <your-default-ssh-port>`
# to get a connection on your first provision. Otherwise, SSH will try connecting 
# to your new port before the SSH server configuration is updated.
ssh_port: "22"

## Email Notifications
postfix_hostname: "{{ inventory_hostname }}"

## Set variables below to enable external SMTP relay
# postfix_relayhost: "smtp.example.com"
# postfix_relayhost_port: "587"
# postfix_relayhost_username: "myusername"
# postfix_relayhost_password: "mysupersecretpassword"

## Configure passwordless sudo for the sudo group (it's disabled by default)
# use_passwordless_sudo: true

##############################################################
# Deploy User
##############################################################
# Docker user configuration.
docker_user:
  username: deploy
  gid: 9999
  group: deploy
  home: "/home/deploy"
  secondary_groups: "docker"
  uid: 9999
  ## Uncomment to set authorized SSH keys for the docker user.
  # authorized_ssh_keys: 
  #   - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNJGtd7a4DBHsQi7HGrC5xz0eAEFHZ3Ogh3FEFI2345 fake@key"
  #   - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRfXxUZ8q9vHRcQZ6tLb0KwGHu8xjQHfYopZKLmnopQ anotherfake@key"

##############################################################
# Users
##############################################################

### Use the template below to set users and their authorized keys
## Passwords are optional if you use passwordless sudo. If you do
## use passwords, you must be set with an encrypted hash. You can 
## generate an encrypted hash with `spin mkpasswd`. Learn more:
## https://serversideup.net/open-source/spin/docs/command-reference/mkpasswd

users:
  - username: spinuser
    name: spinuser
    state: present
    groups: ['adm','sudo']
    password: "$6$s6zqF6Amdv$s1mw09yHtWOy2vQdkVGIe0Rfx24qvbwCd2Y1nY63udoBZ8.SKgm0WWDAuPSTGxMq3oll4XMrjfZZ84ehH1GzN1"
    shell: "/bin/bash"
    authorized_keys:
      - public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7DYxc0L5yQT4AJJM0j+tfVYEfgFLE8cymI0/vNTNBYL+wGtt9CNbtKLAC3KYiPn0dn6DXrfVnQSTCxy06zrahmRRgrRFDURATfqv6tNXA2YQs52i3/CH5ZKYiPVdjYZ81AP0oxxtaSWcWvZIfbXhKAmfVgCFHCJ8zTqruLMAV+mYDhJ/pZxrmD6SNvyLn7StIeS/HsofJ47Dl4NtAzAPF3hPoEryiBY3aqxCn21Pb+JNevFSFhtY8oFKyqkswxT8kalZDIqEuS0RUXoKA7QXLJ8RhnuvdkaQb2Z/+t814SnEVo1xoHObMDNKmsR2DUVAPPOx+4Vks993WWeyUB/MYYtwjh8Zu8ga0bJqrht217WAFKsW++laPZx4+wTitNQHoQU3G9Ibla6c34PVQOJRjkvqdjQKLwO+clW1io8uKsUq7ePkVbbDF3bphb86GRCeYSSjXaT34izKa1kT3V6c6YgDXEu7ChgnKmmQTpn8lB3Zaz2H//N2mxhI9r6XY/kuhpTnsRLHszF/fXWMD2bbFhsJ5Qn98kdsVrfR2k3T5+20eXxoSzrLBte/+cpmGzBKOuCDtRqK+WJgl0CpfAkIdHVpAeIx8H3dOuS8yawo5o5hbwczluxQsT0fRAyTO9PU67TY/VQBIlRZ9FcNaZmMZ6UDPzcoeLkTU8hIMi3N4xQ== user@device"

#   - username: bob
#     name: Bob Smith
#     state: present
#     password: "$6$mysecretsalt$qJbapG68nyRab3gxvKWPUcs2g3t0oMHSHMnSKecYNpSi3CuZm.GbBqXO8BE6EI6P1JUefhA0qvD7b5LSh./PU1"
#     groups: ['adm','sudo']
#     shell: "/bin/bash"
#     authorized_keys:
#       - public_key: "ssh-ed25519 AAAAC3NzaC1anotherfakekeyIMVIzwQXBzxxD9b8Erd1FKVvu bob"

The problem is when I then run spin provision I get:

BECOME password:

PLAY [Setup and provision Docker Swarm servers with Spin.] *******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************************
fatal: [myapp.com]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Bad owner or permissions on /root/.ssh/config", "unreachable": true}
fatal: [staging.myapp.com]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Bad owner or permissions on /root/.ssh/config", "unreachable": true}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************************
myapp.com               : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0
staging.myapp.com       : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

That output would suggest something is wrong with permissions on the .ssh folder or its contents, but I can’t see how that is possible since I am able to ssh in just fine, I double checked permissions and as far as I can tell they are all good.

So I’m not sure if I am misunderstanding what this is trying to do or if there is some issue with running this from within a WSL2 Ubuntu or what. Was really looking forward to testing this thing out.

Thanks in advance for any help.

We run Ansible from a container that mounts /root/.ssh/ to your host machine: spin/lib/functions.sh at 844ec716673cef0313a46051952102f9d09d1c0b · serversideup/spin · GitHub

I would look in your WSL instance and see what the permissions of ~/.ssh/ and the files are underneath it.

Private keys must be 600 permissions.

If you want to generate a new SSH key, you can follow the method of creating a new SSH USER key here: Generating a Secure SSH Key - Spin by Server Side Up

Hope this helps!

Exactly what I needed, thank you for the quick and excellent support experience. Will definitely consider Pro now.

I had already chmod accordingly and it was still not working. In my case it looks like I also had to chown root:$USER the .ssh dir.

You bet! Let me know if you have any more questions.

Thanks for posting the solution too. I might be able to do some things on my end where you don’t need to chown the .ssh dir. This would be a future release of Spin and I would have to do some testing with it.

I do have some follow up questions, if you don’t mind.

  1. In ./spin-inventory.ini:
####################
# Host Types
####################

[production_manager_servers]
myapp.com

[staging_manager_servers]
staging.myapp.com

Will this still work if both prod and staging are actually on the same VPS?

  1. Is it also possible for Spin to handle, again all on the same VPS, a load balancer handling the traffic for 2x production servers?

  2. A fresh VPS dedicated for these servers is recommended. But with that said, is there a place for using Spin for deploying to an environment that already has a reverse proxy? ie, I already have a server with Traefik or Caddy and various small services and want to deploy other small apps conveniently to said server with Spin to keep my workflow consistent across projects. Just curious.

Thanks again for the help, very cool project you got going here.

For 90% of the apps, we recommend 1 server per environment. It gives you a much better experience and more confidence in deploying to production.

For micro apps and apps that you don’t care about downtime as much, it is possible to use pre-existing networks but it can get a little hairy: Networking | Docker Docs

This adds some complexity and additional Docker “know how”.

We’ve done this internally (and we’re cheap bastards when it comes to residual fees :laughing:) but the cost of another $5/mo is totally worth the stability even in the smallest applications.

Just adding a note that I am working on a solution here. Thanks for your report!