diff --git a/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py b/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py
new file mode 100644
index 0000000000000000000000000000000000000000..401717874c34319181c160d28ff88eb426a41589
--- /dev/null
+++ b/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py
@@ -0,0 +1,41 @@
+"""add config version to ip trunk.
+
+Revision ID: 54477431c9ef
+Revises: 465008ed496e
+Create Date: 2025-05-12 04:46:47.410668
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '54477431c9ef'
+down_revision = '465008ed496e'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+INSERT INTO resource_types (resource_type, description) VALUES ('iptrunk_config_version', 'adding option of service version controlled rollout') RETURNING resource_types.resource_type_id
+    """))
+    conn.execute(sa.text("""
+INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IptrunkBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version')))
+    """))
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("""
+DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IptrunkBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IptrunkBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version'))
+    """))
+    conn.execute(sa.text("""
+DELETE FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version')
+    """))
diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json
index ea85bf24e4a64aeb2b5b5ce3203bf0c0e70011b5..2b60d84c809b1911bd081782821f83f0670d2d73 100644
--- a/gso/oss-params-example.json
+++ b/gso/oss-params-example.json
@@ -130,5 +130,21 @@
   "MOODI": {
     "host": "moodi.test.gap.geant.org",
     "moodi_enabled": true
+  },
+  "SERVICE_VERSIONS": {
+    "IP_TRUNK": {
+      "version": {
+        "1.0": "Base Version",
+        "1.1": "Minor Upgrade"
+      },
+      "default_version": "1.0"
+    },
+    "GEANT_IP": {
+      "version": {
+        "1.0": "Base Version",
+        "2.0": "Major Upgrade"
+      },
+      "default_version": "1.0"
+    }
   }
 }
diff --git a/gso/products/product_blocks/iptrunk.py b/gso/products/product_blocks/iptrunk.py
index 8bfc0056a1ee0877f6c8dd388f1643512932cb18..eaaab57fe1931338198235f3d2ecfccf7be803fc 100644
--- a/gso/products/product_blocks/iptrunk.py
+++ b/gso/products/product_blocks/iptrunk.py
@@ -109,6 +109,7 @@ class IptrunkBlockInactive(
     iptrunk_ipv6_network: ipaddress.IPv6Network | None = None
     iptrunk_sides: IptrunkSides[IptrunkSideBlockInactive]
     iptrunk_description_suffix: str | None = None
+    iptrunk_config_version: str | None = None
 
 
 class IptrunkBlockProvisioning(IptrunkBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
@@ -124,6 +125,7 @@ class IptrunkBlockProvisioning(IptrunkBlockInactive, lifecycle=[SubscriptionLife
     iptrunk_ipv6_network: ipaddress.IPv6Network | None
     iptrunk_sides: IptrunkSides[IptrunkSideBlockProvisioning]  # type: ignore[assignment]
     iptrunk_description_suffix: str | None
+    iptrunk_config_version: str | None
 
 
 class IptrunkBlock(IptrunkBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
@@ -152,3 +154,4 @@ class IptrunkBlock(IptrunkBlockProvisioning, lifecycle=[SubscriptionLifecycle.AC
     iptrunk_ipv6_network: ipaddress.IPv6Network
     iptrunk_sides: IptrunkSides[IptrunkSideBlock]  # type: ignore[assignment]
     iptrunk_description_suffix: str | None
+    iptrunk_config_version: str | None
diff --git a/gso/settings.py b/gso/settings.py
index bf5dd53e1793d7f67e2f5b6ca6fa0eb935c69411..8c1b0ec8e2d00c52e89c133ee7474d78289face3 100644
--- a/gso/settings.py
+++ b/gso/settings.py
@@ -228,6 +228,20 @@ class MoodiParams(BaseSettings):
     moodi_enabled: bool = False
 
 
+class ServiceConfig(BaseSettings):
+    """Base configuration object for setting version information of a service."""
+
+    version: dict[str, str]
+    default_version: str
+
+
+class ServiceVersionConfig(BaseSettings):
+    """Services offered by GSO that support multiple versions."""
+
+    IP_TRUNK: ServiceConfig
+    GEANT_IP: ServiceConfig
+
+
 class OSSParams(BaseSettings):
     """The set of parameters required for running GSO."""
 
@@ -242,6 +256,7 @@ class OSSParams(BaseSettings):
     KENTIK: KentikParams
     SENTRY: SentryParams | None = None
     MOODI: MoodiParams
+    SERVICE_VERSIONS: ServiceVersionConfig
 
 
 def load_oss_params() -> OSSParams:
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index c20e8a4e780daff69688fa1b83b7cf57787ce99a..9a73d9c78581666645961ff08b70944446c6b8b7 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -10,7 +10,6 @@ from orchestrator.types import SubscriptionLifecycle
 from pydantic_forms.types import UUIDstr
 from pydantic_forms.validators import Choice
 
-from gso import settings
 from gso.products.product_blocks.layer_2_circuit import Layer2CircuitType
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
@@ -25,6 +24,7 @@ from gso.services.subscriptions import (
     get_router_subscriptions,
     is_virtual_circuit_id_available,
 )
+from gso.settings import load_oss_params
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.interfaces import PhysicalPortCapacity
 from gso.utils.types.ip_address import IPv4AddressType, IPv4NetworkType, IPv6NetworkType
@@ -131,13 +131,13 @@ def iso_from_ipv4(ipv4_address: IPv4AddressType) -> str:
 
 def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str:
     """Generate an FQDN from a hostname, site name, and a country code."""
-    oss = settings.load_oss_params()
+    oss = load_oss_params()
     return f"{hostname}.{site_name.lower()}.{country_code.lower()}{oss.IPAM.LO.domain_name}"
 
 
 def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4NetworkType:
     """Generate an IPv4 network in which a LAN Switch Interconnect resides, given a Site internal ID."""
-    ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
+    ipam_oss = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
 
     result = str(ipam_oss.V4.containers[0]).split(".")[:2]  # Take the first two octets from the IPv4 network.
     result.append(str(site_internal_id))  # Append the side ID as the third octet.
@@ -148,7 +148,7 @@ def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4Net
 
 def generate_lan_switch_interconnect_subnet_v6(site_internal_id: int) -> IPv6NetworkType:
     """Generate an IPv6 network in which a LAN Switch Interconnect resides, given a Site internal ID."""
-    ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
+    ipam_oss = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
 
     result = IPv6Network(ipam_oss.V6.containers[0]).exploded[:17]  # Take the first 56 bits of the network
     result += str(hex(site_internal_id)[2:])  # Append the site internal id for bytes 57 to 64 as hexadecimal number
@@ -283,6 +283,19 @@ def active_edge_port_selector(*, partner_id: UUIDstr | None = None) -> TypeAlias
     )
 
 
+def ip_trunk_service_version_selector() -> TypeAlias:
+    """Generate a dropdown selector for choosing a service version."""
+    iptrunk_versions = load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version
+
+    return cast(
+        type[Choice],
+        Choice.__call__(
+            "Select an IP trunk service version.",
+            [(k, f"Version {k} - {iptrunk_versions[k]}") for k in iptrunk_versions],
+        ),
+    )
+
+
 def partner_choice() -> Choice:
     """Return a Choice object containing a list of available partners."""
     partners = {partner.partner_id: partner.name for partner in get_all_partners()}
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 54bdb6d96c9681e58a2052438a1c8041d9a0edf0..e151068a2f931fd45c0c3f7d4a08279b2d9f2d3e 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -68,6 +68,7 @@ from gso.utils.helpers import (
     available_lags_choices,
     calculate_recommended_minimum_links,
     get_router_vendor,
+    ip_trunk_service_version_selector,
 )
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.interfaces import JuniperLAGMember, LAGMember, LAGMemberList, PhysicalPortCapacity
@@ -95,6 +96,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         iptrunk_speed: PhysicalPortCapacity
         iptrunk_number_of_members: int
         iptrunk_description_suffix: str | None = None
+        iptrunk_config_version: ip_trunk_service_version_selector()  # type: ignore[valid-type]
 
     initial_user_input = yield CreateIptrunkForm
     recommended_minimum_links = calculate_recommended_minimum_links(
@@ -203,6 +205,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         "iptrunk_description",
         "iptrunk_minimum_links",
         "iptrunk_description_suffix",
+        "iptrunk_config_version",
         "side_a_node",
         "side_a_ae_iface",
         "side_a_ae_members",
@@ -327,6 +330,7 @@ def initialize_subscription(
     iptrunk_speed: PhysicalPortCapacity,
     iptrunk_minimum_links: int,
     iptrunk_description_suffix: str | None,
+    iptrunk_config_version: str,
     side_a_node_id: str,
     side_a_ae_iface: str,
     side_a_ae_members: list[dict],
@@ -346,6 +350,7 @@ def initialize_subscription(
     subscription.iptrunk.iptrunk_isis_metric = oss_params.GENERAL.isis_high_metric
     subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
     subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix
+    subscription.iptrunk.iptrunk_config_version = iptrunk_config_version
 
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = side_a
     subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface
diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py
index 58277b0c58f31e8db877370cb941a84ea15527b8..9bb651f5e4bf587e68cf2214aa462166a8818a4a 100644
--- a/gso/workflows/iptrunk/modify_trunk_interface.py
+++ b/gso/workflows/iptrunk/modify_trunk_interface.py
@@ -35,6 +35,7 @@ from gso.utils.helpers import (
     available_interfaces_choices_including_current_members,
     calculate_recommended_minimum_links,
     get_router_vendor,
+    ip_trunk_service_version_selector,
 )
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.interfaces import JuniperLAGMember, LAGMember, LAGMemberList, PhysicalPortCapacity
@@ -95,6 +96,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
             | None
         ) = subscription.iptrunk.gs_id
         iptrunk_description: str | None = subscription.iptrunk.iptrunk_description
+        iptrunk_config_version: ip_trunk_service_version_selector() | str = subscription.iptrunk.iptrunk_config_version  # type: ignore[valid-type]
         iptrunk_type: IptrunkType | str = subscription.iptrunk.iptrunk_type  # FIXME: remove str workaround
         warning_label: Label = (
             "Changing the PhyPortCapacity will result in the deletion of all AE members. "
@@ -268,6 +270,7 @@ def modify_iptrunk_subscription(
     iptrunk_speed: PhysicalPortCapacity,
     iptrunk_minimum_links: int,
     iptrunk_description_suffix: str | None,
+    iptrunk_config_version: str,
     side_a_ga_id: str | None,
     side_a_ae_members: list[dict],
     side_b_ga_id: str | None,
@@ -302,7 +305,7 @@ def modify_iptrunk_subscription(
     subscription.iptrunk.iptrunk_speed = iptrunk_speed
     subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
     subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix
-
+    subscription.iptrunk.iptrunk_config_version = iptrunk_config_version
     subscription.iptrunk.iptrunk_sides[0].ga_id = side_a_ga_id
     update_side_members(subscription, 0, side_a_ae_members)
     subscription.iptrunk.iptrunk_sides[1].ga_id = side_b_ga_id
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index 6996e20a03cf1c69ab0afa7e05e3f3fdd80d60ed..851c3e3081ebc021457d052af8525c63a58cbee0 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -74,6 +74,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker):
         "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND,
         "iptrunk_number_of_members": 2,
         "iptrunk_description_suffix": faker.word(),
+        "iptrunk_config_version": "Version 1.0 - Base Version",
     }
     create_ip_trunk_confirm_step = {"iptrunk_minimum_links": 1}
     create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a}
diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py
index 4e72fbd72893222cd5f6fc516c12dfc08e7b27ca..7c11c95518cf002c25c5178966b5f1706028cdfb 100644
--- a/test/workflows/iptrunk/test_modify_trunk_interface.py
+++ b/test/workflows/iptrunk/test_modify_trunk_interface.py
@@ -121,6 +121,7 @@ def input_form_iptrunk_data(
             "iptrunk_speed": new_speed,
             "iptrunk_number_of_members": new_link_count,
             "iptrunk_description_suffix": faker.word(),
+            "iptrunk_config_version": "1.0",
         },
         {},
         {