diff --git a/alembic_migration/mysql/versions/tdp_lib_1.1_initial_table_creation.py b/alembic_migration/mysql/versions/tdp_lib_1.1_initial_table_creation.py
new file mode 100644
index 00000000..9881387b
--- /dev/null
+++ b/alembic_migration/mysql/versions/tdp_lib_1.1_initial_table_creation.py
@@ -0,0 +1,108 @@
+# Copyright 2022 TOSIT.IO
+# SPDX-License-Identifier: Apache-2.0
+
+"""Initial table creation
+
+Revision ID: tdp_lib_1.1
+Revises:
+Create Date: 2024-06-11 09:59:57.688453
+
+"""
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "tdp_lib_1.1"
+down_revision: Union[str, None] = None
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table(
+        "deployment",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("options", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            sa.Enum(
+                "PLANNED", "RUNNING", "SUCCESS", "FAILURE", name="deploymentstateenum"
+            ),
+            nullable=True,
+        ),
+        sa.Column(
+            "deployment_type",
+            sa.Enum(
+                "DAG",
+                "OPERATIONS",
+                "RESUME",
+                "RECONFIGURE",
+                "CUSTOM",
+                name="deploymenttypeenum",
+            ),
+            nullable=True,
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    op.create_table(
+        "operation",
+        sa.Column("deployment_id", sa.Integer(), nullable=False),
+        sa.Column("operation_order", sa.Integer(), nullable=False),
+        sa.Column("operation", sa.String(length=72), nullable=False),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("extra_vars", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            sa.Enum(
+                "PLANNED",
+                "RUNNING",
+                "PENDING",
+                "SUCCESS",
+                "FAILURE",
+                "HELD",
+                name="operationstateenum",
+            ),
+            nullable=False,
+        ),
+        sa.Column("logs", sa.LargeBinary(length=10000000), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("deployment_id", "operation_order"),
+    )
+    op.create_table(
+        "sch_status_log",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("event_time", sa.DateTime(), nullable=False),
+        sa.Column("service", sa.String(length=20), nullable=False),
+        sa.Column("component", sa.String(length=30), nullable=True),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("running_version", sa.String(length=40), nullable=True),
+        sa.Column("configured_version", sa.String(length=40), nullable=True),
+        sa.Column("to_config", sa.Boolean(), nullable=True),
+        sa.Column("to_restart", sa.Boolean(), nullable=True),
+        sa.Column("is_active", sa.Boolean(), nullable=True),
+        sa.Column(
+            "source",
+            sa.Enum(
+                "DEPLOYMENT", "FORCED", "STALE", "MANUAL", name="schstatuslogsourceenum"
+            ),
+            nullable=False,
+        ),
+        sa.Column("deployment_id", sa.Integer(), nullable=True),
+        sa.Column("message", sa.String(length=512), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    # ### end Alembic commands ###
diff --git a/alembic_migration/postgresql/versions/tdp_lib_1.1_initial_table_creation.py b/alembic_migration/postgresql/versions/tdp_lib_1.1_initial_table_creation.py
new file mode 100644
index 00000000..d4423d4f
--- /dev/null
+++ b/alembic_migration/postgresql/versions/tdp_lib_1.1_initial_table_creation.py
@@ -0,0 +1,144 @@
+# Copyright 2022 TOSIT.IO
+# SPDX-License-Identifier: Apache-2.0
+
+"""Initial table creation
+
+Revision ID: tdp_lib_1.1
+Revises:
+Create Date: 2024-06-11 10:00:08.925442
+
+"""
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision: str = "tdp_lib_1.1"
+down_revision: Union[str, None] = None
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    sa.Enum(
+        "DEPLOYMENT", "FORCED", "STALE", "MANUAL", name="schstatuslogsourceenum"
+    ).create(op.get_bind())
+    sa.Enum(
+        "DAG",
+        "OPERATIONS",
+        "RESUME",
+        "RECONFIGURE",
+        "CUSTOM",
+        name="deploymenttypeenum",
+    ).create(op.get_bind())
+    sa.Enum(
+        "PLANNED", "RUNNING", "SUCCESS", "FAILURE", name="deploymentstateenum"
+    ).create(op.get_bind())
+    sa.Enum(
+        "PLANNED",
+        "RUNNING",
+        "PENDING",
+        "SUCCESS",
+        "FAILURE",
+        "HELD",
+        name="operationstateenum",
+    ).create(op.get_bind())
+    op.create_table(
+        "deployment",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("options", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            postgresql.ENUM(
+                "PLANNED",
+                "RUNNING",
+                "SUCCESS",
+                "FAILURE",
+                name="deploymentstateenum",
+                create_type=False,
+            ),
+            nullable=True,
+        ),
+        sa.Column(
+            "deployment_type",
+            postgresql.ENUM(
+                "DAG",
+                "OPERATIONS",
+                "RESUME",
+                "RECONFIGURE",
+                "CUSTOM",
+                name="deploymenttypeenum",
+                create_type=False,
+            ),
+            nullable=True,
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    op.create_table(
+        "operation",
+        sa.Column("deployment_id", sa.Integer(), nullable=False),
+        sa.Column("operation_order", sa.Integer(), nullable=False),
+        sa.Column("operation", sa.String(length=72), nullable=False),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("extra_vars", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            postgresql.ENUM(
+                "PLANNED",
+                "RUNNING",
+                "PENDING",
+                "SUCCESS",
+                "FAILURE",
+                "HELD",
+                name="operationstateenum",
+                create_type=False,
+            ),
+            nullable=False,
+        ),
+        sa.Column("logs", sa.LargeBinary(length=10000000), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("deployment_id", "operation_order"),
+    )
+    op.create_table(
+        "sch_status_log",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("event_time", sa.DateTime(), nullable=False),
+        sa.Column("service", sa.String(length=20), nullable=False),
+        sa.Column("component", sa.String(length=30), nullable=True),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("running_version", sa.String(length=40), nullable=True),
+        sa.Column("configured_version", sa.String(length=40), nullable=True),
+        sa.Column("to_config", sa.Boolean(), nullable=True),
+        sa.Column("to_restart", sa.Boolean(), nullable=True),
+        sa.Column("is_active", sa.Boolean(), nullable=True),
+        sa.Column(
+            "source",
+            postgresql.ENUM(
+                "DEPLOYMENT",
+                "FORCED",
+                "STALE",
+                "MANUAL",
+                name="schstatuslogsourceenum",
+                create_type=False,
+            ),
+            nullable=False,
+        ),
+        sa.Column("deployment_id", sa.Integer(), nullable=True),
+        sa.Column("message", sa.String(length=512), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    # ### end Alembic commands ###
diff --git a/alembic_migration/sqlite/versions/tdp_lib_1.1_initial_table_creation.py b/alembic_migration/sqlite/versions/tdp_lib_1.1_initial_table_creation.py
new file mode 100644
index 00000000..c53564a3
--- /dev/null
+++ b/alembic_migration/sqlite/versions/tdp_lib_1.1_initial_table_creation.py
@@ -0,0 +1,108 @@
+# Copyright 2022 TOSIT.IO
+# SPDX-License-Identifier: Apache-2.0
+
+"""Initial table creation
+
+Revision ID: tdp_lib_1.1
+Revises:
+Create Date: 2024-06-11 09:59:29.908273
+
+"""
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "tdp_lib_1.1"
+down_revision: Union[str, None] = None
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table(
+        "deployment",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("options", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            sa.Enum(
+                "PLANNED", "RUNNING", "SUCCESS", "FAILURE", name="deploymentstateenum"
+            ),
+            nullable=True,
+        ),
+        sa.Column(
+            "deployment_type",
+            sa.Enum(
+                "DAG",
+                "OPERATIONS",
+                "RESUME",
+                "RECONFIGURE",
+                "CUSTOM",
+                name="deploymenttypeenum",
+            ),
+            nullable=True,
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    op.create_table(
+        "operation",
+        sa.Column("deployment_id", sa.Integer(), nullable=False),
+        sa.Column("operation_order", sa.Integer(), nullable=False),
+        sa.Column("operation", sa.String(length=72), nullable=False),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("extra_vars", sa.JSON(none_as_null=True), nullable=True),
+        sa.Column("start_time", sa.DateTime(), nullable=True),
+        sa.Column("end_time", sa.DateTime(), nullable=True),
+        sa.Column(
+            "state",
+            sa.Enum(
+                "PLANNED",
+                "RUNNING",
+                "PENDING",
+                "SUCCESS",
+                "FAILURE",
+                "HELD",
+                name="operationstateenum",
+            ),
+            nullable=False,
+        ),
+        sa.Column("logs", sa.LargeBinary(length=10000000), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("deployment_id", "operation_order"),
+    )
+    op.create_table(
+        "sch_status_log",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("event_time", sa.DateTime(), nullable=False),
+        sa.Column("service", sa.String(length=20), nullable=False),
+        sa.Column("component", sa.String(length=30), nullable=True),
+        sa.Column("host", sa.String(length=255), nullable=True),
+        sa.Column("running_version", sa.String(length=40), nullable=True),
+        sa.Column("configured_version", sa.String(length=40), nullable=True),
+        sa.Column("to_config", sa.Boolean(), nullable=True),
+        sa.Column("to_restart", sa.Boolean(), nullable=True),
+        sa.Column("is_active", sa.Boolean(), nullable=True),
+        sa.Column(
+            "source",
+            sa.Enum(
+                "DEPLOYMENT", "FORCED", "STALE", "MANUAL", name="schstatuslogsourceenum"
+            ),
+            nullable=False,
+        ),
+        sa.Column("deployment_id", sa.Integer(), nullable=True),
+        sa.Column("message", sa.String(length=512), nullable=True),
+        sa.ForeignKeyConstraint(
+            ["deployment_id"],
+            ["deployment.id"],
+        ),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    # ### end Alembic commands ###
diff --git a/tdp/cli/commands/init.py b/tdp/cli/commands/init.py
index dc52c46f..64fe6f38 100644
--- a/tdp/cli/commands/init.py
+++ b/tdp/cli/commands/init.py
@@ -3,11 +3,14 @@
 
 from __future__ import annotations
 
+import logging
 from pathlib import Path
 from typing import TYPE_CHECKING
 
 import click
-from sqlalchemy import Engine
+from alembic.command import stamp, upgrade
+from alembic.config import Config
+from sqlalchemy import MetaData, Table, create_engine
 
 from tdp.cli.params import (
     collections_option,
@@ -21,6 +24,8 @@
 if TYPE_CHECKING:
     from tdp.core.collections import Collections
 
+logging.getLogger("alembic").setLevel(logging.ERROR)
+
 
 @click.command()
 @click.option(
@@ -31,19 +36,49 @@
     multiple=True,
     help="Path to TDP variables overrides. Can be used multiple times. Last one takes precedence.",
 )
+@click.argument(
+    "alembic_ini_path",
+    envvar="ALEMBIC_CONFIG",
+    required=True,
+    type=click.Path(exists=True, resolve_path=True, path_type=Path),
+)
 @collections_option
-@database_dsn_option
+@database_dsn_option(create_engine=False)
 @validate_option
 @vars_option(exists=False)
 def init(
     overrides: tuple[Path],
     collections: Collections,
-    db_engine: Engine,
+    alembic_ini_path: Path,
+    db_engine: str,
     validate: bool,
     vars: Path,
 ):
     """Initialize the database and the TDP variables."""
-    init_database(db_engine)
+    engine = create_engine(db_engine)
+    with engine.connect() as connection:
+        alembic_config = Config(
+            file_=str(alembic_ini_path),
+            ini_section=engine.dialect.name,
+        )
+        alembic_config.set_main_option("sqlalchemy.url", db_engine)
+
+        try:
+            alembic_version = Table("alembic_version", MetaData(), autoload_with=engine)
+            db_revision_id_row = connection.execute(alembic_version.select()).fetchone()
+            db_revision_id = db_revision_id_row[0] if db_revision_id_row else None
+
+        except:
+            db_revision_id = None
+
+        # Upgrade database if new revision in migration folder, else create all tables or fail if revisions don't coincide.
+        if db_revision_id:
+            upgrade(config=alembic_config, revision="head")
+            click.echo(f"Upgrade tables to revision ID: {db_revision_id}")
+        else:
+            init_database(engine)
+            stamp(config=alembic_config, revision="head")
+    # Create the cluster variables.
     cluster_variables = ClusterVariables.initialize_cluster_variables(
         collections, vars, overrides, validate=validate
     )
diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py
index 9f3cc909..dfec5fd8 100644
--- a/tests/e2e/conftest.py
+++ b/tests/e2e/conftest.py
@@ -7,7 +7,7 @@
 
 import pytest
 from click.testing import CliRunner
-from sqlalchemy import create_engine
+from sqlalchemy import MetaData, Table, create_engine
 
 from tdp.cli.commands.init import init
 from tdp.core.models import BaseModel
@@ -32,11 +32,13 @@ def tdp_init(
         "--vars",
         str(vars),
     ]
+    env = {"ALEMBIC_CONFIG": str(Path("alembic.ini").resolve())}
     runner = CliRunner()
-    runner.invoke(init, base_args)
+    runner.invoke(init, base_args, env=env)
     yield TDPInitArgs(collection_path, db_dsn, vars)
     engine = create_engine(db_dsn)
     BaseModel.metadata.drop_all(engine)
+    Table("alembic_version", MetaData(), autoload_with=engine).drop(engine)
     engine.dispose()
 
 
diff --git a/tests/e2e/test_tdp_init.py b/tests/e2e/test_tdp_init.py
index 1ee435e7..92d92bc8 100644
--- a/tests/e2e/test_tdp_init.py
+++ b/tests/e2e/test_tdp_init.py
@@ -19,7 +19,8 @@ def test_tdp_init_db_is_created(collection_path: Path, vars: Path, tmp_path: Pat
         "--vars",
         str(vars),
     ]
+    env = {"ALEMBIC_CONFIG": str(Path("alembic.ini").resolve())}
     runner = CliRunner()
-    result = runner.invoke(init, args)
+    result = runner.invoke(init, args, env=env)
     assert os.path.exists(db_path) == True
     assert result.exit_code == 0, result.output