diff --git a/.gitignore b/.gitignore
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c6dee3f2dc3dd149c7dc5c29a9f43cd0d70194d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.pyc
+*.swp
+.venv
+.vscode
+venv
+.DS_Store
+.idea
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d784f2b0b47daaefdfac4b197368e209b3c6dcec
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,32 @@
+test:
+  stage: test
+  image: python:$PYTHON_VERSION
+  script:
+    - pip3 install -r requirements.txt
+    - python3 runtests.py
+  parallel:
+    matrix:
+      - PYTHON_VERSION: [
+          "3.9-slim",
+          "3.10-slim",
+          "3.11-slim",
+        ]
+
+
+
+upload:
+  stage: build
+  needs: ['test', ]
+  image: harbor.pasteur.fr/kube-system/helm-kubectl:3.4.0
+  script:
+    - CHART_VERSION=$(helm show chart shiny-server | grep version | cut -d' ' -f2)
+    - CHART_NAME=$(helm show chart shiny-server | grep name | cut -d' ' -f2)
+    - |
+      if [ "helm" == "${CI_COMMIT_REF_SLUG}" ]; then
+        export CHANNEL="stable"
+      else
+        export CHANNEL="${CI_COMMIT_REF_SLUG}"
+      fi
+      echo $CHANNEL
+    - helm package shiny-server
+    - curl --request POST --user gitlab-ci-token:$CI_JOB_TOKEN --form "chart=@${CHART_NAME}-${CHART_VERSION}.tgz" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/${CHANNEL}/charts"
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..9c8317c4510812c42d632f2b905fc8fdd88e0c4d
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include LICENSE
+include README.rst
\ No newline at end of file
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a6d27f3e1f492ec5f0a7e1df6b6e4e0cb5120c1c
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,26 @@
+============
+django-kubernetes-probes
+============
+
+django-kubernetes-probes is a Django app to conduct web-based polls. For each
+question, visitors can choose between a fixed number of answers.
+
+Detailed documentation is in the "docs" directory.
+
+Quick start
+-----------
+
+1. Add "polls" to your INSTALLED_APPS setting like this::
+
+    INSTALLED_APPS = [
+        ...,
+        "django_kubernetes_probes",
+    ]
+
+2. Include the polls URLconf in your project urls.py like this::
+
+    path("polls/", include("django_kubernetes_probes.urls")),
+
+4. Start the development server.
+
+5. Visit the ``/probe_alive/`` URL to see that you app is alive.
\ No newline at end of file
diff --git a/django_kubernetes_probes/__init__.py b/django_kubernetes_probes/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/django_kubernetes_probes/apps.py b/django_kubernetes_probes/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9e5cb0135cc6f0a691d33cb8fe4829b7ee152fb
--- /dev/null
+++ b/django_kubernetes_probes/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class ProbesConfig(AppConfig):
+    default_auto_field = "django.db.models.BigAutoField"
+    name = "django_kubernetes_probes"
+    label = "probes"
diff --git a/django_kubernetes_probes/tests.py b/django_kubernetes_probes/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..410a0d038b5f66323d4669f77d0eb37a86e144fa
--- /dev/null
+++ b/django_kubernetes_probes/tests.py
@@ -0,0 +1,12 @@
+import logging
+
+from django.test import TestCase
+from django.urls import reverse
+
+logger = logging.getLogger(__name__)
+
+
+class ProbeTestCase(TestCase):
+    def test_is_pk(self):
+        self.assertEqual(200, self.client.get(reverse("probes:probe_ready")).status_code)
+        self.assertEqual(200, self.client.get(reverse("probes:probe_alive")).status_code)
diff --git a/django_kubernetes_probes/urls.py b/django_kubernetes_probes/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..eadf71ee9588994e954e1786af079fcd507612b8
--- /dev/null
+++ b/django_kubernetes_probes/urls.py
@@ -0,0 +1,12 @@
+from django.urls import path
+
+from probes import views
+
+app_name = "probes"
+urlpatterns = [
+    ##############################
+    # url(r'^$', views.index, name='home'),
+    ##############################
+    path("probe_alive/", views.is_alive, name="probe_alive"),
+    path("probe_ready/", views.is_ready, name="probe_ready"),
+]
diff --git a/django_kubernetes_probes/views.py b/django_kubernetes_probes/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..13181d5ea52493e12f87fc39200667061b0e81a2
--- /dev/null
+++ b/django_kubernetes_probes/views.py
@@ -0,0 +1,28 @@
+from django.contrib.auth import get_user_model
+from django.http import HttpResponse
+
+
+def is_ready(request):
+    from django.db import OperationalError
+    import django.apps
+
+    try:
+        for model_class in django.apps.apps.get_models():
+            if not model_class._meta.managed:
+                continue  # we don't test not managed models
+            model_class.objects.first()
+        return HttpResponse(status=200)
+    except OperationalError:
+        pass
+    return HttpResponse(status=500)
+
+
+def is_alive(request):
+    from django.db import OperationalError
+
+    try:
+        get_user_model().objects.exists()
+        return HttpResponse(status=200)
+    except OperationalError:
+        pass
+    return HttpResponse(status=500)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..c515c8282697bd89e57f492a7d14ce6ce3c051ca
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ['setuptools>=40.8.0']
+build-backend = 'setuptools.build_meta'
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7b9fd672009e9b0346e0450fffd082ff50070a8e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+django>=3.2
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e4d5ec8f1fb3e487debcde7b793a43af131c0202
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,31 @@
+[metadata]
+name = django-kubernetes-probes
+version = 1.0
+description = A Django app to expose probes for kubernetes
+long_description = file: README.rst
+url = https://www.example.com/
+author = Bryan Brancotte
+author_email = bryan.brancotte@pasteur.fr
+license = BSD-3-Clause  # Example license
+classifiers =
+    Environment :: Web Environment
+    Framework :: Django
+    Framework :: Django :: X.Y  # Replace "X.Y" as appropriate
+    Intended Audience :: Developers
+    License :: OSI Approved :: BSD License
+    Operating System :: OS Independent
+    Programming Language :: Python
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3 :: Only
+    Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
+    Programming Language :: Python :: 3.12
+    Topic :: Internet :: WWW/HTTP
+    Topic :: Internet :: WWW/HTTP :: Dynamic Content
+
+[options]
+include_package_data = true
+packages = find:
+python_requires = >=3.10
+install_requires =
+    Django >= X.Y  # Replace "X.Y" as appropriate
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc1f76c84d17b458f7090667d495592c9abda034
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,3 @@
+from setuptools import setup
+
+setup()
\ No newline at end of file