diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
new file mode 100755
index 000000000..656d3a3c4
--- /dev/null
+++ b/jenkins/Jenkinsfile
@@ -0,0 +1,152 @@
+def projects = ['ahtc', 'guez']
+
+// Maps OsVersion -> BUILD_VERSION env var consumed by rpmbuild/make_rpm.py
+// (the script looks for lts${BUILD_VERSION}.spec; empty selects specs/lts.spec).
+// Ubuntu 26.04 is the target platform for Qt6 migration
+def spec_suffix_for_os = [
+ '26.04': '26.04'
+]
+
+def choice_projects = ['ALL'] + projects
+properties([
+ disableConcurrentBuilds(),
+ parameters([
+ choice(name: 'ProjectName', choices: choice_projects, description: 'LTS project flavor (passed as cmake -D_pname)'),
+ string(name: 'OsVersions', defaultValue: '26.04', description: 'Comma-separated Ubuntu base versions, all built in parallel (selects build container tag and spec variant)'),
+ string(name: 'LtsVersion', defaultValue: '1.0', description: 'LTS version (major.minor) passed to make_rpm.py --version'),
+ booleanParam(name: 'ClearWorkspace', defaultValue: false, description: 'Clear entire workspace on all nodes used for build'),
+ booleanParam(name: 'PublishArtifactRepo', defaultValue: true, description: 'Upload produced RPMs to the internal artifact repository')
+ ])
+])
+
+env.GIT_TERMINAL_PROMPT = "0"
+env.LTS_VERSION = "${params.LtsVersion}"
+env.BUILD_CONTAINER_REGISTRY = "harbor.sl.defence-elec.de"
+env.BUILD_CONTAINER_PREFIX = "buildcontainer"
+env.ARTIFACTREPO = "https://internal-repo.sl.defence-elec.de"
+env.BITBUCKET_URL = "https://devhub.defence-elec.de/bitbucket"
+
+// Posts the build status to Bitbucket via jenkins/jenkins_notifier.sh (REST
+// build-status API). A notification failure must never break the build itself.
+def notifyBitbucketStatus(String state) {
+ try {
+ withCredentials([usernamePassword(credentialsId: 'bitbucket-notifier',
+ usernameVariable: 'CURL_USER',
+ passwordVariable: 'CURL_PASS')]) {
+ sh """
+ export CURL_CRED="\${CURL_USER}:\${CURL_PASS}"
+ export BUILD_STATE='${state}'
+ bash jenkins/jenkins_notifier.sh
+ """
+ }
+ } catch (e) {
+ echo "WARNING: Bitbucket notification (${state}) failed: ${e.message}"
+ }
+}
+
+// OS versions this build covers; every (project, osVersion) pair runs in parallel.
+def osVersions = params.OsVersions.split(',').collect { it.trim() }.findAll { it }
+
+if (params.ProjectName != 'ALL') {
+ projects = [params.ProjectName]
+}
+
+
+// One parallel branch per (project, osVersion) combination. Outputs are isolated
+// under rpms/${project}/${osVersion} so concurrent OS builds never collide.
+def builds = [:]
+projects.each { project ->
+ osVersions.each { osVersion ->
+ def buildVersion = spec_suffix_for_os.containsKey(osVersion) ? spec_suffix_for_os[osVersion] : osVersion
+ def rpmsDir = "rpms/${project}/${osVersion}"
+
+ builds["${project} (${osVersion})"] = {
+ stage("Prepare ${project} (${osVersion})") {
+ sh "rm -rf ${rpmsDir}"
+ sh "mkdir -p ${rpmsDir}"
+ }
+
+ stage("Build RPM ${project} (${osVersion})") {
+ def imageName = "${BUILD_CONTAINER_REGISTRY}/${BUILD_CONTAINER_PREFIX}/${project}_${osVersion}"
+ def buildContainer = docker.image("${imageName}")
+ def guezMode = (project == 'guez') ? 'ON' : 'OFF'
+
+ def dockerOpts = "--init --shm-size=2g" +
+ " -e HOME=/shd/CTC/TOOLS" +
+ " -e PROJECT_BASE_DIR=/tmp/legatus" +
+ " -e PROJECT=${project}" +
+ " -e OS_VERSION=${osVersion}" +
+ " -e LTS_VERSION=${LTS_VERSION}" +
+ " -e GUEZ_MODE=${guezMode}" +
+ " -e BUILD_VERSION=${buildVersion}" +
+ " -e BUILD_NUMBER=${env.BUILD_NUMBER}" +
+ " -e BUILD_TAG=${env.BUILD_TAG}"
+
+ buildContainer.inside(dockerOpts) {
+ sh "bash jenkins/jenkins_builder.sh"
+ }
+ }
+
+ stage("Publish ${project} (${osVersion})") {
+ if (params.PublishArtifactRepo) {
+ withCredentials([usernamePassword(credentialsId: 'internal-repo-uploader',
+ usernameVariable: 'CURL_USER',
+ passwordVariable: 'CURL_PASS')]) {
+ sh """
+ export CURL_CRED="\${CURL_USER}:\${CURL_PASS}"
+ export PROJECT='${project}'
+ export OS_VERSION='${osVersion}'
+ export LTS_VERSION='${LTS_VERSION}'
+ export BUILD_NUMBER='${env.BUILD_NUMBER}'
+ export ARTIFACTREPO='${env.ARTIFACTREPO}'
+ export ARTIFACTFOLDER='${project}'
+ bash jenkins/jenkins_publisher.sh
+ """
+ }
+
+ def tdsUrl = readFile("url-${project}-${osVersion}.txt").trim()
+ def tdsVersion = readFile("tdsversion-${project}-${osVersion}.txt").trim()
+ def link = """${project} ${tdsVersion} (OS ${osVersion})"""
+ currentBuild.description = currentBuild.description ? "${currentBuild.description}
${link}" : link
+ } else {
+ echo "Artifact-repo publish skipped for ${project} (OS ${osVersion}, PublishArtifactRepo=false)"
+ }
+ }
+
+ stage("Cleanup ${project} (${osVersion})") {
+ sh "rm -rf ${rpmsDir}"
+ }
+ }
+ }
+}
+
+notifyBitbucketStatus('INPROGRESS')
+
+try {
+ stage("Initialization") {
+ if (params.ClearWorkspace) {
+ cleanWs()
+ checkout([
+ $class: 'GitSCM',
+ branches: scm.branches,
+ doGenerateSubmoduleConfigurations: false,
+ extensions: [[$class: 'SubmoduleOption', disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: '', trackingSubmodules: false]],
+ userRemoteConfigs: scm.userRemoteConfigs
+ ])
+ }
+ echo "Legatus build initialized — branch '${env.BRANCH_NAME}', project(s): ${projects.join(', ')}, LTS ${env.LTS_VERSION}, OS version(s) (parallel): ${osVersions.join(', ')}"
+ }
+
+ parallel builds
+
+ stage("Finalization") {
+ echo "Legatus build completed — branch '${env.BRANCH_NAME}', project(s): ${projects.join(', ')} (LTS ${env.LTS_VERSION}), OS version(s): ${osVersions.join(', ')}"
+ }
+
+ currentBuild.result = 'SUCCESS'
+} catch (e) {
+ currentBuild.result = 'FAILURE'
+ throw e
+} finally {
+ notifyBitbucketStatus(currentBuild.result == 'SUCCESS' ? 'SUCCESSFUL' : 'FAILED')
+}