diff --git a/Vagrantfile b/Vagrantfile
index c26c627..4f4c7c0 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -28,6 +28,7 @@ def provision(vm, role, node_num)
     ansible.extra_vars = {
       k3s_version: "v1.28.14+k3s1",
       api_endpoint: "#{NETWORK_PREFIX}.100",
+      # Required for vagrant ansible provisioner
       token: "myvagrant",
       # Required to use the private network configured above
       extra_server_args: "--node-external-ip #{node_ip} --flannel-iface eth1", 
diff --git a/inventory-sample.yml b/inventory-sample.yml
index 8c09d3f..e237881 100644
--- a/inventory-sample.yml
+++ b/inventory-sample.yml
@@ -19,6 +19,7 @@ k3s_cluster:
     # - openssl rand -base64 64
     # - pwgen -s 64 1
     # You can use ansible-vault to encrypt this value / keep it secret.
+    # Or you can omit it if not using Vagrant and let the first server automatically generate one.
     token: "changeme!"
     api_endpoint: "{{ hostvars[groups['server'][0]]['ansible_host'] | default(groups['server'][0]) }}"
     extra_server_args: ""
diff --git a/roles/k3s_agent/defaults/main.yml b/roles/k3s_agent/defaults/main.yml
index cf5acb9..c190cc6 100644
--- a/roles/k3s_agent/defaults/main.yml
+++ b/roles/k3s_agent/defaults/main.yml
@@ -1,4 +1,5 @@
 ---
+server_group: server  # noqa var-naming[no-role-prefix]
 k3s_server_location: "/var/lib/rancher/k3s"  # noqa var-naming[no-role-prefix]
 systemd_dir: "/etc/systemd/system"  # noqa var-naming[no-role-prefix]
 api_port: 6443  # noqa var-naming[no-role-prefix]
diff --git a/roles/k3s_agent/tasks/main.yml b/roles/k3s_agent/tasks/main.yml
index 39df326..c7f9b96 100644
--- a/roles/k3s_agent/tasks/main.yml
+++ b/roles/k3s_agent/tasks/main.yml
@@ -35,6 +35,10 @@
         INSTALL_K3S_EXEC: "agent"
       changed_when: true
 
+- name: Get the token from the first server
+  ansible.builtin.set_fact:
+    token: "{{ hostvars[groups[server_group][0]].token }}"
+
 - name: Delete any existing token from the environment if different from the new one
   ansible.builtin.lineinfile:
     state: absent
diff --git a/roles/k3s_server/tasks/main.yml b/roles/k3s_server/tasks/main.yml
index 8331190..4c434b6 100644
--- a/roles/k3s_server/tasks/main.yml
+++ b/roles/k3s_server/tasks/main.yml
@@ -90,14 +90,16 @@
       ansible.builtin.lineinfile:
         state: absent
         path: "{{ systemd_dir }}/k3s.service.env"
-        regexp: "^K3S_TOKEN=\\s*(?!{{ token }}\\s*$)"
+        regexp: "^K3S_TOKEN=\\s*(?!{{ token | default('') }}\\s*$)"
 
-    # Add the token to the environment.
+    # Add the token to the environment if it has been provided.
+    # Otherwise, let the first server create one on the first run.
     - name: Add token as an environment variable
       no_log: true # avoid logging the server token
       ansible.builtin.lineinfile:
         path: "{{ systemd_dir }}/k3s.service.env"
         line: "K3S_TOKEN={{ token }}"
+      when: token is defined
 
     - name: Restart K3s service
       when:
@@ -182,11 +184,31 @@
           changed_when:
             - mv_result.rc == 0
 
+    - name: Get the token if randomly generated
+      when: token is not defined
+      block:
+        - name: Wait for token
+          ansible.builtin.wait_for:
+            path: /var/lib/rancher/k3s/server/token
+
+        - name: Read node-token from master
+          ansible.builtin.slurp:
+            src: /var/lib/rancher/k3s/server/token
+          register: node_token
+
+        - name: Store Master node-token
+          ansible.builtin.set_fact:
+            token: "{{ node_token.content | b64decode | regex_replace('\n', '') }}"
+
 - name: Start other server if any and verify status
   when:
     - (groups[server_group] | length) > 1
     - inventory_hostname != groups[server_group][0]
   block:
+    - name: Get the token from the first server
+      ansible.builtin.set_fact:
+        token: "{{ hostvars[groups[server_group][0]].token }}"
+
     - name: Delete any existing token from the environment if different from the new one
       ansible.builtin.lineinfile:
         state: absent