diff --git a/arkindex/documents/fixtures/data.json b/arkindex/documents/fixtures/data.json
index afec6b200f5288e91efbbfa7d288506ab37e844b..1f63c8fe43c51bc2a7afda9ffc41e89f744dd328 100644
--- a/arkindex/documents/fixtures/data.json
+++ b/arkindex/documents/fixtures/data.json
@@ -3777,6 +3777,7 @@
         "updated": "2020-02-02T01:23:45.678Z",
         "expiry": "2050-03-03T01:23:45.678Z",
         "extra_files": "{}",
+        "token": "MjViMzE5NDQtNzc2YS00YThjLWE1YWUtY2RhYTY2ZmE0OTIzCg==",
         "parents": []
     }
 },
diff --git a/arkindex/ponos/migrations/0037_task_token.py b/arkindex/ponos/migrations/0037_task_token.py
new file mode 100644
index 0000000000000000000000000000000000000000..6eb06f62febf766846e11cf43c8f66938126daae
--- /dev/null
+++ b/arkindex/ponos/migrations/0037_task_token.py
@@ -0,0 +1,39 @@
+# Generated by Django 4.1.7 on 2023-03-07 15:19
+
+from django.db import migrations, models
+
+from arkindex.ponos.models import task_token_default
+
+
+def add_task_tokens(apps, schema_editor):
+    Task = apps.get_model('ponos', 'Task')
+    to_update = []
+    for task in Task.objects.filter(token=None).only('id').iterator():
+        task.token = task_token_default()
+        to_update.append(task)
+    Task.objects.bulk_update(to_update, ['token'], batch_size=100)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ponos', '0036_hstore_task_env_and_extra_files'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='task',
+            name='token',
+            field=models.CharField(
+                default=task_token_default,
+                max_length=52,
+                # Make the field temporarily nullable and not unique, so that we can
+                # fill the tokens on existing tasks before adding the constraints.
+                null=True,
+            ),
+        ),
+        migrations.RunPython(
+            add_task_tokens,
+            reverse_code=migrations.RunPython.noop,
+        ),
+    ]
diff --git a/arkindex/ponos/migrations/0038_task_token_unique.py b/arkindex/ponos/migrations/0038_task_token_unique.py
new file mode 100644
index 0000000000000000000000000000000000000000..5de95c85ee7afba67531028d0fd1fb9eaac1c586
--- /dev/null
+++ b/arkindex/ponos/migrations/0038_task_token_unique.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.1.7 on 2023-03-07 15:25
+
+from django.db import migrations, models
+
+from arkindex.ponos.models import task_token_default
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ponos', '0037_task_token'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='task',
+            name='token',
+            field=models.CharField(
+                default=task_token_default,
+                max_length=52,
+                unique=True,
+            ),
+        ),
+    ]
diff --git a/arkindex/ponos/models.py b/arkindex/ponos/models.py
index f6a9cd2d6259580f10d1603fff642b593695d352..8ede5e8e428ea26f02c43c83395cedbdec9d7ac4 100644
--- a/arkindex/ponos/models.py
+++ b/arkindex/ponos/models.py
@@ -1,3 +1,4 @@
+import base64
 import logging
 import os.path
 import random
@@ -605,6 +606,15 @@ def expiry_default():
     return timezone.now() + timedelta(days=30)
 
 
+def task_token_default():
+    """
+    Default value for Task.token.
+
+    :rtype: str
+    """
+    return base64.encodebytes(uuid.uuid4().bytes).strip()
+
+
 class Task(models.Model):
     """
     A task created from a workflow's recipe.
@@ -670,6 +680,13 @@ class Task(models.Model):
     # Remote files required to start the container
     extra_files = HStoreField(default=dict)
 
+    token = models.CharField(
+        default=task_token_default,
+        # The token generation always returns 52 characters
+        max_length=52,
+        unique=True,
+    )
+
     objects = TaskManager()
 
     class Meta: