[{"data":1,"prerenderedAt":4061},["ShallowReactive",2],{"blog-current-shopware-plugin-gitlab-pipeline-build-en":3,"blog-previous-shopware-plugin-gitlab-pipeline-build-en":1208,"blog-next-shopware-plugin-gitlab-pipeline-build-en":1219,"blog-alt-de-shopware-plugin-gitlab-pipeline-build-en":1230,"blog-alt-en-shopware-plugin-gitlab-pipeline-build-en":1232,"employee-robert-juzak":1233,"content-query-TPh1lFGN9c":1332,"content-query-GCsGFnjSrZ":2821,"content-query-KtO3wftRle":3976,"related-refs-shopware_devops--en":4011},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"author":10,"image":11,"releaseDate":12,"blogCategories":13,"articleTags":16,"tags":19,"body":22,"_type":1202,"_id":1203,"_source":1204,"_file":1205,"_stem":1206,"_extension":1207},"/en/blog/shopware-plugin-gitlab-pipeline-build","blog",false,"","Test, build and release a Shopware 6 Plugin with GitLab CI - Part 2: build","Part 2 - Build - of using GitLab for testing, building and releasing a Shopware 6 Plugin","robert-juzak","/images/dev-ops-dark.svg","2026-02-26",[14,15],"What moves us","DevOps",[15,17,18],"Open Source","Shopware",[20,21],"shopware","devops",{"type":23,"children":24,"toc":1198},"root",[25,34,50,63,70,75,113,118,123,143,156,161,182,188,208,575,587,592,1158,1168,1173,1192],{"type":26,"tag":27,"props":28,"children":33},"element","img",{"alt":7,"aspect-ratio":29,"height":30,"object-fit":31,"src":32},"1.78",300,"fill","/blog/shopware-plugin-build+release.png",[],{"type":26,"tag":35,"props":36,"children":37},"p",{},[38,41,48],{"type":39,"value":40},"text","In my ",{"type":26,"tag":42,"props":43,"children":45},"a",{"href":44},"/en/blog/shopware-plugin-gitlab-pipeline-release",[46],{"type":39,"value":47},"previous post",{"type":39,"value":49}," I described how to distribute a Shopware 6 plugin over GitLab Package registry.",{"type":26,"tag":35,"props":51,"children":52},{},[53,55,61],{"type":39,"value":54},"When building our project using ",{"type":26,"tag":42,"props":56,"children":58},{"href":57},"https://developer.shopware.com/docs/products/cli/extension-commands/build.html#building-an-extension",[59],{"type":39,"value":60},"shopware-cli",{"type":39,"value":62},",\nthe tool will look over all plugins and check if they need to be built and do so if needed. This is an unnecessary step.",{"type":26,"tag":64,"props":65,"children":67},"h2",{"id":66},"building-manually",[68],{"type":39,"value":69},"Building manually",{"type":26,"tag":35,"props":71,"children":72},{},[73],{"type":39,"value":74},"Building a plugin is as simple as running:",{"type":26,"tag":76,"props":77,"children":81},"pre",{"className":78,"code":79,"language":80,"meta":7,"style":7},"language-shell shiki shiki-themes github-dark github-dark monokai","shopware-cli extension build .\n","shell",[82],{"type":26,"tag":83,"props":84,"children":85},"code",{"__ignoreMap":7},[86],{"type":26,"tag":87,"props":88,"children":91},"span",{"class":89,"line":90},"line",1,[92,97,103,108],{"type":26,"tag":87,"props":93,"children":95},{"style":94},"--shiki-default:#B392F0;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E",[96],{"type":39,"value":60},{"type":26,"tag":87,"props":98,"children":100},{"style":99},"--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74",[101],{"type":39,"value":102}," extension",{"type":26,"tag":87,"props":104,"children":105},{"style":99},[106],{"type":39,"value":107}," build",{"type":26,"tag":87,"props":109,"children":110},{"style":99},[111],{"type":39,"value":112}," .\n",{"type":26,"tag":35,"props":114,"children":115},{},[116],{"type":39,"value":117},"In the plugin root directory.",{"type":26,"tag":35,"props":119,"children":120},{},[121],{"type":39,"value":122},"There are some tweaks you can make, like:",{"type":26,"tag":124,"props":125,"children":126},"ul",{},[127,133,138],{"type":26,"tag":128,"props":129,"children":130},"li",{},[131],{"type":39,"value":132},"constrain a shopware version",{"type":26,"tag":128,"props":134,"children":135},{},[136],{"type":39,"value":137},"specify extra bundles",{"type":26,"tag":128,"props":139,"children":140},{},[141],{"type":39,"value":142},"use esbuild",{"type":26,"tag":35,"props":144,"children":145},{},[146,148,154],{"type":39,"value":147},"Please refer to the ",{"type":26,"tag":42,"props":149,"children":151},{"href":150},"https://developer.shopware.com/docs/products/cli/extension-commands/build.html",[152],{"type":39,"value":153},"official documentation",{"type":39,"value":155}," for detailed configuration.",{"type":26,"tag":35,"props":157,"children":158},{},[159],{"type":39,"value":160},"The build process will create the following directories containing the compiled files:",{"type":26,"tag":124,"props":162,"children":163},{},[164,173],{"type":26,"tag":128,"props":165,"children":166},{},[167],{"type":26,"tag":83,"props":168,"children":170},{"className":169},[],[171],{"type":39,"value":172},"src/Resources/app/storefront/dist/",{"type":26,"tag":128,"props":174,"children":175},{},[176],{"type":26,"tag":83,"props":177,"children":179},{"className":178},[],[180],{"type":39,"value":181},"src/Resources/public/static/",{"type":26,"tag":64,"props":183,"children":185},{"id":184},"build-pipeline",[186],{"type":39,"value":187},"Build pipeline",{"type":26,"tag":35,"props":189,"children":190},{},[191,193,198,200,206],{"type":39,"value":192},"We use the official ",{"type":26,"tag":83,"props":194,"children":196},{"className":195},[],[197],{"type":39,"value":60},{"type":39,"value":199}," docker image. To speed up the process, we take advantage of the ",{"type":26,"tag":83,"props":201,"children":203},{"className":202},[],[204],{"type":39,"value":205},"CI",{"type":39,"value":207}," caching system.",{"type":26,"tag":76,"props":209,"children":221},{"className":210,"code":211,"filename":212,"highlights":213,"language":220,"meta":7,"style":7},"language-yaml shiki shiki-themes github-dark github-dark monokai","stages:\n  - build\n\nbuild:\n  image:\n    name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n    entrypoint: [\"\"]\n  stage: build\n  variables:\n    COMPOSER_CACHE_DIR: ${CI_PROJECT_DIR}/.composer\n    npm_config_cache: ${CI_PROJECT_DIR}/.npm\n  script:\n    - shopware-cli extension build .\n  cache:\n    - key: $CI_JOB_NAME\n      paths:\n        - $COMPOSER_CACHE_DIR\n        - $npm_config_cache\n  rules:\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: never\n    - if: $CI_COMMIT_BRANCH\n","\u003Cplugin-root>/.gitlab-ci.yml",[214,215,216,217,218,219],6,14,15,16,17,18,"yaml",[222],{"type":26,"tag":83,"props":223,"children":224},{"__ignoreMap":7},[225,240,254,264,277,290,310,334,351,364,382,400,413,426,439,461,474,488,501,514,536,554],{"type":26,"tag":87,"props":226,"children":227},{"class":89,"line":90},[228,234],{"type":26,"tag":87,"props":229,"children":231},{"style":230},"--shiki-default:#85E89D;--shiki-dark:#85E89D;--shiki-sepia:#F92672",[232],{"type":39,"value":233},"stages",{"type":26,"tag":87,"props":235,"children":237},{"style":236},"--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2",[238],{"type":39,"value":239},":\n",{"type":26,"tag":87,"props":241,"children":243},{"class":89,"line":242},2,[244,249],{"type":26,"tag":87,"props":245,"children":246},{"style":236},[247],{"type":39,"value":248},"  - ",{"type":26,"tag":87,"props":250,"children":251},{"style":99},[252],{"type":39,"value":253},"build\n",{"type":26,"tag":87,"props":255,"children":257},{"class":89,"line":256},3,[258],{"type":26,"tag":87,"props":259,"children":261},{"emptyLinePlaceholder":260},true,[262],{"type":39,"value":263},"\n",{"type":26,"tag":87,"props":265,"children":267},{"class":89,"line":266},4,[268,273],{"type":26,"tag":87,"props":269,"children":270},{"style":230},[271],{"type":39,"value":272},"build",{"type":26,"tag":87,"props":274,"children":275},{"style":236},[276],{"type":39,"value":239},{"type":26,"tag":87,"props":278,"children":280},{"class":89,"line":279},5,[281,286],{"type":26,"tag":87,"props":282,"children":283},{"style":230},[284],{"type":39,"value":285},"  image",{"type":26,"tag":87,"props":287,"children":288},{"style":236},[289],{"type":39,"value":239},{"type":26,"tag":87,"props":291,"children":294},{"class":292,"line":214},[89,293],"highlight",[295,300,305],{"type":26,"tag":87,"props":296,"children":297},{"style":230},[298],{"type":39,"value":299},"    name",{"type":26,"tag":87,"props":301,"children":302},{"style":236},[303],{"type":39,"value":304},": ",{"type":26,"tag":87,"props":306,"children":307},{"style":99},[308],{"type":39,"value":309},"ghcr.io/shopware/shopware-cli:latest-php-8.2\n",{"type":26,"tag":87,"props":311,"children":313},{"class":89,"line":312},7,[314,319,324,329],{"type":26,"tag":87,"props":315,"children":316},{"style":230},[317],{"type":39,"value":318},"    entrypoint",{"type":26,"tag":87,"props":320,"children":321},{"style":236},[322],{"type":39,"value":323},": [",{"type":26,"tag":87,"props":325,"children":326},{"style":99},[327],{"type":39,"value":328},"\"\"",{"type":26,"tag":87,"props":330,"children":331},{"style":236},[332],{"type":39,"value":333},"]\n",{"type":26,"tag":87,"props":335,"children":337},{"class":89,"line":336},8,[338,343,347],{"type":26,"tag":87,"props":339,"children":340},{"style":230},[341],{"type":39,"value":342},"  stage",{"type":26,"tag":87,"props":344,"children":345},{"style":236},[346],{"type":39,"value":304},{"type":26,"tag":87,"props":348,"children":349},{"style":99},[350],{"type":39,"value":253},{"type":26,"tag":87,"props":352,"children":354},{"class":89,"line":353},9,[355,360],{"type":26,"tag":87,"props":356,"children":357},{"style":230},[358],{"type":39,"value":359},"  variables",{"type":26,"tag":87,"props":361,"children":362},{"style":236},[363],{"type":39,"value":239},{"type":26,"tag":87,"props":365,"children":367},{"class":89,"line":366},10,[368,373,377],{"type":26,"tag":87,"props":369,"children":370},{"style":230},[371],{"type":39,"value":372},"    COMPOSER_CACHE_DIR",{"type":26,"tag":87,"props":374,"children":375},{"style":236},[376],{"type":39,"value":304},{"type":26,"tag":87,"props":378,"children":379},{"style":99},[380],{"type":39,"value":381},"${CI_PROJECT_DIR}/.composer\n",{"type":26,"tag":87,"props":383,"children":385},{"class":89,"line":384},11,[386,391,395],{"type":26,"tag":87,"props":387,"children":388},{"style":230},[389],{"type":39,"value":390},"    npm_config_cache",{"type":26,"tag":87,"props":392,"children":393},{"style":236},[394],{"type":39,"value":304},{"type":26,"tag":87,"props":396,"children":397},{"style":99},[398],{"type":39,"value":399},"${CI_PROJECT_DIR}/.npm\n",{"type":26,"tag":87,"props":401,"children":403},{"class":89,"line":402},12,[404,409],{"type":26,"tag":87,"props":405,"children":406},{"style":230},[407],{"type":39,"value":408},"  script",{"type":26,"tag":87,"props":410,"children":411},{"style":236},[412],{"type":39,"value":239},{"type":26,"tag":87,"props":414,"children":416},{"class":89,"line":415},13,[417,422],{"type":26,"tag":87,"props":418,"children":419},{"style":236},[420],{"type":39,"value":421},"    - ",{"type":26,"tag":87,"props":423,"children":424},{"style":99},[425],{"type":39,"value":79},{"type":26,"tag":87,"props":427,"children":429},{"class":428,"line":215},[89,293],[430,435],{"type":26,"tag":87,"props":431,"children":432},{"style":230},[433],{"type":39,"value":434},"  cache",{"type":26,"tag":87,"props":436,"children":437},{"style":236},[438],{"type":39,"value":239},{"type":26,"tag":87,"props":440,"children":442},{"class":441,"line":216},[89,293],[443,447,452,456],{"type":26,"tag":87,"props":444,"children":445},{"style":236},[446],{"type":39,"value":421},{"type":26,"tag":87,"props":448,"children":449},{"style":230},[450],{"type":39,"value":451},"key",{"type":26,"tag":87,"props":453,"children":454},{"style":236},[455],{"type":39,"value":304},{"type":26,"tag":87,"props":457,"children":458},{"style":99},[459],{"type":39,"value":460},"$CI_JOB_NAME\n",{"type":26,"tag":87,"props":462,"children":464},{"class":463,"line":217},[89,293],[465,470],{"type":26,"tag":87,"props":466,"children":467},{"style":230},[468],{"type":39,"value":469},"      paths",{"type":26,"tag":87,"props":471,"children":472},{"style":236},[473],{"type":39,"value":239},{"type":26,"tag":87,"props":475,"children":477},{"class":476,"line":218},[89,293],[478,483],{"type":26,"tag":87,"props":479,"children":480},{"style":236},[481],{"type":39,"value":482},"        - ",{"type":26,"tag":87,"props":484,"children":485},{"style":99},[486],{"type":39,"value":487},"$COMPOSER_CACHE_DIR\n",{"type":26,"tag":87,"props":489,"children":491},{"class":490,"line":219},[89,293],[492,496],{"type":26,"tag":87,"props":493,"children":494},{"style":236},[495],{"type":39,"value":482},{"type":26,"tag":87,"props":497,"children":498},{"style":99},[499],{"type":39,"value":500},"$npm_config_cache\n",{"type":26,"tag":87,"props":502,"children":504},{"class":89,"line":503},19,[505,510],{"type":26,"tag":87,"props":506,"children":507},{"style":230},[508],{"type":39,"value":509},"  rules",{"type":26,"tag":87,"props":511,"children":512},{"style":236},[513],{"type":39,"value":239},{"type":26,"tag":87,"props":515,"children":517},{"class":89,"line":516},20,[518,522,527,531],{"type":26,"tag":87,"props":519,"children":520},{"style":236},[521],{"type":39,"value":421},{"type":26,"tag":87,"props":523,"children":524},{"style":230},[525],{"type":39,"value":526},"if",{"type":26,"tag":87,"props":528,"children":529},{"style":236},[530],{"type":39,"value":304},{"type":26,"tag":87,"props":532,"children":533},{"style":99},[534],{"type":39,"value":535},"$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n",{"type":26,"tag":87,"props":537,"children":539},{"class":89,"line":538},21,[540,545,549],{"type":26,"tag":87,"props":541,"children":542},{"style":230},[543],{"type":39,"value":544},"      when",{"type":26,"tag":87,"props":546,"children":547},{"style":236},[548],{"type":39,"value":304},{"type":26,"tag":87,"props":550,"children":551},{"style":99},[552],{"type":39,"value":553},"never\n",{"type":26,"tag":87,"props":555,"children":557},{"class":89,"line":556},22,[558,562,566,570],{"type":26,"tag":87,"props":559,"children":560},{"style":236},[561],{"type":39,"value":421},{"type":26,"tag":87,"props":563,"children":564},{"style":230},[565],{"type":39,"value":526},{"type":26,"tag":87,"props":567,"children":568},{"style":236},[569],{"type":39,"value":304},{"type":26,"tag":87,"props":571,"children":572},{"style":99},[573],{"type":39,"value":574},"$CI_COMMIT_BRANCH\n",{"type":26,"tag":35,"props":576,"children":577},{},[578,580,585],{"type":39,"value":579},"Let's combine it with our release pipeline from the ",{"type":26,"tag":42,"props":581,"children":583},{"href":582},"/en/blog/shopware-plugin-gitlab-pipeline-release#with-semantic-release",[584],{"type":39,"value":47},{"type":39,"value":586},".",{"type":26,"tag":35,"props":588,"children":589},{},[590],{"type":39,"value":591},"It's important to pass the built artifacts over to the next job.",{"type":26,"tag":76,"props":593,"children":597},{"className":210,"code":594,"filename":212,"highlights":595,"language":220,"meta":7,"style":7},"stages:\n  - release\n  - build\n\nbuild:\n  image:\n    name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n    entrypoint: [\"\"]\n  stage: build\n  variables:\n    COMPOSER_CACHE_DIR: ${CI_PROJECT_DIR}/.composer\n    npm_config_cache: ${CI_PROJECT_DIR}/.npm\n  script:\n    - shopware-cli extension build .\n  cache:\n    - key: $CI_JOB_NAME\n      paths:\n        - $COMPOSER_CACHE_DIR\n        - $npm_config_cache\n  artifacts:\n    paths:\n      - src/Resources/public\n      - src/Storefront/Resources/public\n  rules:\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: never\n    - if: $CI_COMMIT_BRANCH\n\nrelease:\n  stage: release\n  image:\n    name: ghcr.io/voxpupuli/semantic-release:latest\n    entrypoint: [\"\"]\n  interruptible: true\n  script:\n    - /docker-entrypoint.sh\n  rules:\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: never\n    - if: $CI_COMMIT_BRANCH\n",[516,538,556,596],23,[598],{"type":26,"tag":83,"props":599,"children":600},{"__ignoreMap":7},[601,612,624,635,642,653,664,679,698,713,724,739,754,765,776,787,806,817,828,839,852,865,879,892,904,924,940,960,968,981,997,1009,1026,1046,1065,1077,1090,1102,1122,1138],{"type":26,"tag":87,"props":602,"children":603},{"class":89,"line":90},[604,608],{"type":26,"tag":87,"props":605,"children":606},{"style":230},[607],{"type":39,"value":233},{"type":26,"tag":87,"props":609,"children":610},{"style":236},[611],{"type":39,"value":239},{"type":26,"tag":87,"props":613,"children":614},{"class":89,"line":242},[615,619],{"type":26,"tag":87,"props":616,"children":617},{"style":236},[618],{"type":39,"value":248},{"type":26,"tag":87,"props":620,"children":621},{"style":99},[622],{"type":39,"value":623},"release\n",{"type":26,"tag":87,"props":625,"children":626},{"class":89,"line":256},[627,631],{"type":26,"tag":87,"props":628,"children":629},{"style":236},[630],{"type":39,"value":248},{"type":26,"tag":87,"props":632,"children":633},{"style":99},[634],{"type":39,"value":253},{"type":26,"tag":87,"props":636,"children":637},{"class":89,"line":266},[638],{"type":26,"tag":87,"props":639,"children":640},{"emptyLinePlaceholder":260},[641],{"type":39,"value":263},{"type":26,"tag":87,"props":643,"children":644},{"class":89,"line":279},[645,649],{"type":26,"tag":87,"props":646,"children":647},{"style":230},[648],{"type":39,"value":272},{"type":26,"tag":87,"props":650,"children":651},{"style":236},[652],{"type":39,"value":239},{"type":26,"tag":87,"props":654,"children":655},{"class":89,"line":214},[656,660],{"type":26,"tag":87,"props":657,"children":658},{"style":230},[659],{"type":39,"value":285},{"type":26,"tag":87,"props":661,"children":662},{"style":236},[663],{"type":39,"value":239},{"type":26,"tag":87,"props":665,"children":666},{"class":89,"line":312},[667,671,675],{"type":26,"tag":87,"props":668,"children":669},{"style":230},[670],{"type":39,"value":299},{"type":26,"tag":87,"props":672,"children":673},{"style":236},[674],{"type":39,"value":304},{"type":26,"tag":87,"props":676,"children":677},{"style":99},[678],{"type":39,"value":309},{"type":26,"tag":87,"props":680,"children":681},{"class":89,"line":336},[682,686,690,694],{"type":26,"tag":87,"props":683,"children":684},{"style":230},[685],{"type":39,"value":318},{"type":26,"tag":87,"props":687,"children":688},{"style":236},[689],{"type":39,"value":323},{"type":26,"tag":87,"props":691,"children":692},{"style":99},[693],{"type":39,"value":328},{"type":26,"tag":87,"props":695,"children":696},{"style":236},[697],{"type":39,"value":333},{"type":26,"tag":87,"props":699,"children":700},{"class":89,"line":353},[701,705,709],{"type":26,"tag":87,"props":702,"children":703},{"style":230},[704],{"type":39,"value":342},{"type":26,"tag":87,"props":706,"children":707},{"style":236},[708],{"type":39,"value":304},{"type":26,"tag":87,"props":710,"children":711},{"style":99},[712],{"type":39,"value":253},{"type":26,"tag":87,"props":714,"children":715},{"class":89,"line":366},[716,720],{"type":26,"tag":87,"props":717,"children":718},{"style":230},[719],{"type":39,"value":359},{"type":26,"tag":87,"props":721,"children":722},{"style":236},[723],{"type":39,"value":239},{"type":26,"tag":87,"props":725,"children":726},{"class":89,"line":384},[727,731,735],{"type":26,"tag":87,"props":728,"children":729},{"style":230},[730],{"type":39,"value":372},{"type":26,"tag":87,"props":732,"children":733},{"style":236},[734],{"type":39,"value":304},{"type":26,"tag":87,"props":736,"children":737},{"style":99},[738],{"type":39,"value":381},{"type":26,"tag":87,"props":740,"children":741},{"class":89,"line":402},[742,746,750],{"type":26,"tag":87,"props":743,"children":744},{"style":230},[745],{"type":39,"value":390},{"type":26,"tag":87,"props":747,"children":748},{"style":236},[749],{"type":39,"value":304},{"type":26,"tag":87,"props":751,"children":752},{"style":99},[753],{"type":39,"value":399},{"type":26,"tag":87,"props":755,"children":756},{"class":89,"line":415},[757,761],{"type":26,"tag":87,"props":758,"children":759},{"style":230},[760],{"type":39,"value":408},{"type":26,"tag":87,"props":762,"children":763},{"style":236},[764],{"type":39,"value":239},{"type":26,"tag":87,"props":766,"children":767},{"class":89,"line":215},[768,772],{"type":26,"tag":87,"props":769,"children":770},{"style":236},[771],{"type":39,"value":421},{"type":26,"tag":87,"props":773,"children":774},{"style":99},[775],{"type":39,"value":79},{"type":26,"tag":87,"props":777,"children":778},{"class":89,"line":216},[779,783],{"type":26,"tag":87,"props":780,"children":781},{"style":230},[782],{"type":39,"value":434},{"type":26,"tag":87,"props":784,"children":785},{"style":236},[786],{"type":39,"value":239},{"type":26,"tag":87,"props":788,"children":789},{"class":89,"line":217},[790,794,798,802],{"type":26,"tag":87,"props":791,"children":792},{"style":236},[793],{"type":39,"value":421},{"type":26,"tag":87,"props":795,"children":796},{"style":230},[797],{"type":39,"value":451},{"type":26,"tag":87,"props":799,"children":800},{"style":236},[801],{"type":39,"value":304},{"type":26,"tag":87,"props":803,"children":804},{"style":99},[805],{"type":39,"value":460},{"type":26,"tag":87,"props":807,"children":808},{"class":89,"line":218},[809,813],{"type":26,"tag":87,"props":810,"children":811},{"style":230},[812],{"type":39,"value":469},{"type":26,"tag":87,"props":814,"children":815},{"style":236},[816],{"type":39,"value":239},{"type":26,"tag":87,"props":818,"children":819},{"class":89,"line":219},[820,824],{"type":26,"tag":87,"props":821,"children":822},{"style":236},[823],{"type":39,"value":482},{"type":26,"tag":87,"props":825,"children":826},{"style":99},[827],{"type":39,"value":487},{"type":26,"tag":87,"props":829,"children":830},{"class":89,"line":503},[831,835],{"type":26,"tag":87,"props":832,"children":833},{"style":236},[834],{"type":39,"value":482},{"type":26,"tag":87,"props":836,"children":837},{"style":99},[838],{"type":39,"value":500},{"type":26,"tag":87,"props":840,"children":842},{"class":841,"line":516},[89,293],[843,848],{"type":26,"tag":87,"props":844,"children":845},{"style":230},[846],{"type":39,"value":847},"  artifacts",{"type":26,"tag":87,"props":849,"children":850},{"style":236},[851],{"type":39,"value":239},{"type":26,"tag":87,"props":853,"children":855},{"class":854,"line":538},[89,293],[856,861],{"type":26,"tag":87,"props":857,"children":858},{"style":230},[859],{"type":39,"value":860},"    paths",{"type":26,"tag":87,"props":862,"children":863},{"style":236},[864],{"type":39,"value":239},{"type":26,"tag":87,"props":866,"children":868},{"class":867,"line":556},[89,293],[869,874],{"type":26,"tag":87,"props":870,"children":871},{"style":236},[872],{"type":39,"value":873},"      - ",{"type":26,"tag":87,"props":875,"children":876},{"style":99},[877],{"type":39,"value":878},"src/Resources/public\n",{"type":26,"tag":87,"props":880,"children":882},{"class":881,"line":596},[89,293],[883,887],{"type":26,"tag":87,"props":884,"children":885},{"style":236},[886],{"type":39,"value":873},{"type":26,"tag":87,"props":888,"children":889},{"style":99},[890],{"type":39,"value":891},"src/Storefront/Resources/public\n",{"type":26,"tag":87,"props":893,"children":895},{"class":89,"line":894},24,[896,900],{"type":26,"tag":87,"props":897,"children":898},{"style":230},[899],{"type":39,"value":509},{"type":26,"tag":87,"props":901,"children":902},{"style":236},[903],{"type":39,"value":239},{"type":26,"tag":87,"props":905,"children":907},{"class":89,"line":906},25,[908,912,916,920],{"type":26,"tag":87,"props":909,"children":910},{"style":236},[911],{"type":39,"value":421},{"type":26,"tag":87,"props":913,"children":914},{"style":230},[915],{"type":39,"value":526},{"type":26,"tag":87,"props":917,"children":918},{"style":236},[919],{"type":39,"value":304},{"type":26,"tag":87,"props":921,"children":922},{"style":99},[923],{"type":39,"value":535},{"type":26,"tag":87,"props":925,"children":927},{"class":89,"line":926},26,[928,932,936],{"type":26,"tag":87,"props":929,"children":930},{"style":230},[931],{"type":39,"value":544},{"type":26,"tag":87,"props":933,"children":934},{"style":236},[935],{"type":39,"value":304},{"type":26,"tag":87,"props":937,"children":938},{"style":99},[939],{"type":39,"value":553},{"type":26,"tag":87,"props":941,"children":943},{"class":89,"line":942},27,[944,948,952,956],{"type":26,"tag":87,"props":945,"children":946},{"style":236},[947],{"type":39,"value":421},{"type":26,"tag":87,"props":949,"children":950},{"style":230},[951],{"type":39,"value":526},{"type":26,"tag":87,"props":953,"children":954},{"style":236},[955],{"type":39,"value":304},{"type":26,"tag":87,"props":957,"children":958},{"style":99},[959],{"type":39,"value":574},{"type":26,"tag":87,"props":961,"children":963},{"class":89,"line":962},28,[964],{"type":26,"tag":87,"props":965,"children":966},{"emptyLinePlaceholder":260},[967],{"type":39,"value":263},{"type":26,"tag":87,"props":969,"children":971},{"class":89,"line":970},29,[972,977],{"type":26,"tag":87,"props":973,"children":974},{"style":230},[975],{"type":39,"value":976},"release",{"type":26,"tag":87,"props":978,"children":979},{"style":236},[980],{"type":39,"value":239},{"type":26,"tag":87,"props":982,"children":984},{"class":89,"line":983},30,[985,989,993],{"type":26,"tag":87,"props":986,"children":987},{"style":230},[988],{"type":39,"value":342},{"type":26,"tag":87,"props":990,"children":991},{"style":236},[992],{"type":39,"value":304},{"type":26,"tag":87,"props":994,"children":995},{"style":99},[996],{"type":39,"value":623},{"type":26,"tag":87,"props":998,"children":1000},{"class":89,"line":999},31,[1001,1005],{"type":26,"tag":87,"props":1002,"children":1003},{"style":230},[1004],{"type":39,"value":285},{"type":26,"tag":87,"props":1006,"children":1007},{"style":236},[1008],{"type":39,"value":239},{"type":26,"tag":87,"props":1010,"children":1012},{"class":89,"line":1011},32,[1013,1017,1021],{"type":26,"tag":87,"props":1014,"children":1015},{"style":230},[1016],{"type":39,"value":299},{"type":26,"tag":87,"props":1018,"children":1019},{"style":236},[1020],{"type":39,"value":304},{"type":26,"tag":87,"props":1022,"children":1023},{"style":99},[1024],{"type":39,"value":1025},"ghcr.io/voxpupuli/semantic-release:latest\n",{"type":26,"tag":87,"props":1027,"children":1029},{"class":89,"line":1028},33,[1030,1034,1038,1042],{"type":26,"tag":87,"props":1031,"children":1032},{"style":230},[1033],{"type":39,"value":318},{"type":26,"tag":87,"props":1035,"children":1036},{"style":236},[1037],{"type":39,"value":323},{"type":26,"tag":87,"props":1039,"children":1040},{"style":99},[1041],{"type":39,"value":328},{"type":26,"tag":87,"props":1043,"children":1044},{"style":236},[1045],{"type":39,"value":333},{"type":26,"tag":87,"props":1047,"children":1049},{"class":89,"line":1048},34,[1050,1055,1059],{"type":26,"tag":87,"props":1051,"children":1052},{"style":230},[1053],{"type":39,"value":1054},"  interruptible",{"type":26,"tag":87,"props":1056,"children":1057},{"style":236},[1058],{"type":39,"value":304},{"type":26,"tag":87,"props":1060,"children":1062},{"style":1061},"--shiki-default:#79B8FF;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF",[1063],{"type":39,"value":1064},"true\n",{"type":26,"tag":87,"props":1066,"children":1068},{"class":89,"line":1067},35,[1069,1073],{"type":26,"tag":87,"props":1070,"children":1071},{"style":230},[1072],{"type":39,"value":408},{"type":26,"tag":87,"props":1074,"children":1075},{"style":236},[1076],{"type":39,"value":239},{"type":26,"tag":87,"props":1078,"children":1080},{"class":89,"line":1079},36,[1081,1085],{"type":26,"tag":87,"props":1082,"children":1083},{"style":236},[1084],{"type":39,"value":421},{"type":26,"tag":87,"props":1086,"children":1087},{"style":99},[1088],{"type":39,"value":1089},"/docker-entrypoint.sh\n",{"type":26,"tag":87,"props":1091,"children":1093},{"class":89,"line":1092},37,[1094,1098],{"type":26,"tag":87,"props":1095,"children":1096},{"style":230},[1097],{"type":39,"value":509},{"type":26,"tag":87,"props":1099,"children":1100},{"style":236},[1101],{"type":39,"value":239},{"type":26,"tag":87,"props":1103,"children":1105},{"class":89,"line":1104},38,[1106,1110,1114,1118],{"type":26,"tag":87,"props":1107,"children":1108},{"style":236},[1109],{"type":39,"value":421},{"type":26,"tag":87,"props":1111,"children":1112},{"style":230},[1113],{"type":39,"value":526},{"type":26,"tag":87,"props":1115,"children":1116},{"style":236},[1117],{"type":39,"value":304},{"type":26,"tag":87,"props":1119,"children":1120},{"style":99},[1121],{"type":39,"value":535},{"type":26,"tag":87,"props":1123,"children":1125},{"class":89,"line":1124},39,[1126,1130,1134],{"type":26,"tag":87,"props":1127,"children":1128},{"style":230},[1129],{"type":39,"value":544},{"type":26,"tag":87,"props":1131,"children":1132},{"style":236},[1133],{"type":39,"value":304},{"type":26,"tag":87,"props":1135,"children":1136},{"style":99},[1137],{"type":39,"value":553},{"type":26,"tag":87,"props":1139,"children":1141},{"class":89,"line":1140},40,[1142,1146,1150,1154],{"type":26,"tag":87,"props":1143,"children":1144},{"style":236},[1145],{"type":39,"value":421},{"type":26,"tag":87,"props":1147,"children":1148},{"style":230},[1149],{"type":39,"value":526},{"type":26,"tag":87,"props":1151,"children":1152},{"style":236},[1153],{"type":39,"value":304},{"type":26,"tag":87,"props":1155,"children":1156},{"style":99},[1157],{"type":39,"value":574},{"type":26,"tag":35,"props":1159,"children":1160},{},[1161],{"type":26,"tag":87,"props":1162,"children":1165},{"className":1163},[1164],"text-h2",[1166],{"type":39,"value":1167},"That's it!",{"type":26,"tag":35,"props":1169,"children":1170},{},[1171],{"type":39,"value":1172},"This will:",{"type":26,"tag":1174,"props":1175,"children":1176},"ol",{},[1177,1182,1187],{"type":26,"tag":128,"props":1178,"children":1179},{},[1180],{"type":39,"value":1181},"Build all the assets",{"type":26,"tag":128,"props":1183,"children":1184},{},[1185],{"type":39,"value":1186},"Pass them to the second job",{"type":26,"tag":128,"props":1188,"children":1189},{},[1190],{"type":39,"value":1191},"Run the release process as described previously",{"type":26,"tag":1193,"props":1194,"children":1195},"style",{},[1196],{"type":39,"value":1197},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":7,"searchDepth":242,"depth":242,"links":1199},[1200,1201],{"id":66,"depth":242,"text":69},{"id":184,"depth":242,"text":187},"markdown","common:en:blog:19.shopware-plugin-gitlab-pipeline-build.md","common","en/blog/19.shopware-plugin-gitlab-pipeline-build.md","en/blog/19.shopware-plugin-gitlab-pipeline-build","md",{"_path":1209,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1210,"description":1211,"author":10,"image":11,"releaseDate":1212,"blogCategories":1213,"articleTags":1214,"tags":1215,"_type":1202,"_id":1216,"_source":1204,"_file":1217,"_stem":1218,"_extension":1207},"/en/blog/gitops-docker-renovate","Software management with GitLab, Renovate Bot and Docker","Managing software on a server is not easy. Is it?","2025-10-30",[14,15],[15,17],[21],"common:en:blog:17.gitops-docker-renovate.md","en/blog/17.gitops-docker-renovate.md","en/blog/17.gitops-docker-renovate",{"_path":1220,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1221,"description":1222,"author":10,"image":11,"releaseDate":1223,"blogCategories":1224,"articleTags":1225,"tags":1226,"_type":1202,"_id":1227,"_source":1204,"_file":1228,"_stem":1229,"_extension":1207},"/en/blog/traefik-magic","Using Traefik as reverse proxy - convention over configuration","How to configure Traefik reverse proxy to get rid of any Traefik specific labels and automatically route to subdomains matching compose project name","2026-03-13",[14,15],[15],[21],"common:en:blog:21.traefik-magic.md","en/blog/21.traefik-magic.md","en/blog/21.traefik-magic",{"_path":1231},"/blog/shopware-plugin-gitlab-pipeline-build",{"_path":4},{"_path":1234,"_dir":1235,"_draft":6,"_partial":6,"_locale":7,"slug":10,"teams":1236,"primaryTeam":1238,"firstName":1239,"lastName":1240,"prefixTitle":7,"suffixTitle":1241,"education":1242,"role":1247,"workingSince":1252,"inTheCompanySince":1253,"techSkills":1254,"skills":1293,"projects":1311,"contactDetails":1321,"_image":1325,"image":1326,"_id":1327,"_type":1328,"title":1329,"_source":1235,"_file":1330,"_stem":1331,"_extension":1328},"/employees/robert-juzak","employees",[1237,1238],"appDev","devOps","Robert","Juzak","B.Sc.",[1243],[1244,1245,1246],"Bachelor of Computer Science","Technische Universität Breslau","2016",[1248,1249,1250,1251],"softwareDeveloper","fullstackDeveloper","admin","consultant","2015","2018",[1255,1259,1261,1264,1267,1270,1272,1275,1278,1282,1285,1287,1290],{"name":1256,"level":1257,"icon":1258},"Docker","expert","/images/Docker.svg",{"name":1260,"level":1257},"GitLab",{"name":1262,"level":1257,"icon":1263},"Kubernetes","/images/Kubernetes.svg",{"name":1265,"level":1257,"icon":1266},"PHPUnit","/images/PHP-Unit.svg",{"name":1268,"level":1257,"icon":1269},"Portainer","/images/Portainer.svg",{"name":1271,"level":1257},"Sentry",{"name":1273,"level":1257,"icon":1274},"Sonarqube","/images/Sonarqube.svg",{"name":1276,"level":1257,"icon":1277},"Linux","/images/linux_os-mono.svg",{"name":1279,"level":1280,"icon":1281},"CSS","advanced","/images/css.svg",{"name":1283,"level":1280,"icon":1284},"HTML","/images/html.svg",{"name":1286,"level":1280},"PHP",{"name":1288,"level":1280,"icon":1289},"SQL","/images/SQL.svg",{"name":1291,"level":1280,"icon":1292},"VueJS","/images/vuejs.svg",[1294,1296,1298,1299,1301,1303,1305,1307,1309],{"name":1295,"level":1257},"CI/CD",{"name":1297,"level":1257},"qualityAssurance",{"name":1238,"level":1257},{"name":1300,"level":1257},"testDrivenBugfix",{"name":1302,"level":1257},"testDrivenDevelopment",{"name":1304,"level":1280},"accessibility",{"name":1306,"level":1280},"databases",{"name":1308,"level":1280},"linuxServerAdministration",{"name":1310,"level":1280},"softwareArchitect",[1312,1317,1319],{"project":1313,"position":1314},"Herole",[1315,1316],"Dev-Ops","Frontend Developer",{"project":1318,"position":1316},"Huawei-Calibration-aaS",{"project":1320,"position":1316},"Huawei-Inspect-3D",{"eMail":1322,"phone":1323,"visibility":1324},"robert.juzak@helmundwalter.de","+49 351 799 035 26","1","images/employees/Portraits/robert_juzak.webp","images/employees/Portraits/RobertJuzak_MS.webp","employees:employees:6.robert-juzak.json","json","Robert Juzak","employees/6.robert-juzak.json","employees/6.robert-juzak",{"_path":1209,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1210,"description":1211,"author":10,"image":11,"releaseDate":1212,"blogCategories":1333,"articleTags":1334,"tags":1335,"body":1336,"_type":1202,"_id":1216,"_source":1204,"_file":1217,"_stem":1218,"_extension":1207},[14,15],[15,17],[21],{"type":23,"children":1337,"toc":2814},[1338,1343,1348,1353,1358,1363,1381,1386,1428,1434,1447,1460,1465,1471,1484,1497,1506,1512,1517,1630,2154,2190,2201,2205,2210,2373,2386,2392,2397,2475,2480,2486,2491,2502,2515,2549,2576,2588,2609,2622,2625,2630,2643,2661,2666,2671,2792,2810],{"type":26,"tag":35,"props":1339,"children":1340},{},[1341],{"type":39,"value":1342},"Managing a multiservice server can be tricky. Not every software is compatible with another, for example different requirements for database or PHP-versions.",{"type":26,"tag":35,"props":1344,"children":1345},{},[1346],{"type":39,"value":1347},"Additionally, all the software should be in the latest version, to mitigate possible security risks.",{"type":26,"tag":35,"props":1349,"children":1350},{},[1351],{"type":39,"value":1352},"There are many ways to handle this: Ansible, Chef etc.",{"type":26,"tag":35,"props":1354,"children":1355},{},[1356],{"type":39,"value":1357},"Out goal was to have an easy to use, automated and free solution.",{"type":26,"tag":35,"props":1359,"children":1360},{},[1361],{"type":39,"value":1362},"Here are the goals:",{"type":26,"tag":124,"props":1364,"children":1365},{},[1366,1371,1376],{"type":26,"tag":128,"props":1367,"children":1368},{},[1369],{"type":39,"value":1370},"use the GitOps approach to store and version control the deployment",{"type":26,"tag":128,"props":1372,"children":1373},{},[1374],{"type":39,"value":1375},"use containers to run the software",{"type":26,"tag":128,"props":1377,"children":1378},{},[1379],{"type":39,"value":1380},"get automated security updates and opt in for minor/major version updates",{"type":26,"tag":35,"props":1382,"children":1383},{},[1384],{"type":39,"value":1385},"This is the stack we ended with:",{"type":26,"tag":124,"props":1387,"children":1388},{},[1389,1407,1417],{"type":26,"tag":128,"props":1390,"children":1391},{},[1392,1397,1399,1405],{"type":26,"tag":42,"props":1393,"children":1395},{"href":1394},"https://www.docker.com/",[1396],{"type":39,"value":1256},{"type":39,"value":1398}," and ",{"type":26,"tag":42,"props":1400,"children":1402},{"href":1401},"https://docs.docker.com/compose/",[1403],{"type":39,"value":1404},"Docker Compose",{"type":39,"value":1406}," do manage the software",{"type":26,"tag":128,"props":1408,"children":1409},{},[1410,1415],{"type":26,"tag":42,"props":1411,"children":1413},{"href":1412},"https://about.gitlab.com/",[1414],{"type":39,"value":1260},{"type":39,"value":1416}," to store all the compose files",{"type":26,"tag":128,"props":1418,"children":1419},{},[1420,1426],{"type":26,"tag":42,"props":1421,"children":1423},{"href":1422},"https://docs.renovatebot.com",[1424],{"type":39,"value":1425},"Renovate Bot",{"type":39,"value":1427}," to keep the software up to date",{"type":26,"tag":64,"props":1429,"children":1431},{"id":1430},"about-the-stack",[1432],{"type":39,"value":1433},"About the stack",{"type":26,"tag":35,"props":1435,"children":1436},{},[1437,1439,1445],{"type":39,"value":1438},"We have been using Docker in production for some time now. Depending on the situation, we create the compose file directly on the server, manage it over portainer or ",{"type":26,"tag":83,"props":1440,"children":1442},{"className":1441},[],[1443],{"type":39,"value":1444},"scp",{"type":39,"value":1446}," it from a pipeline.",{"type":26,"tag":35,"props":1448,"children":1449},{},[1450,1452,1458],{"type":39,"value":1451},"GitLab is out primary tool for version control. Additionally, a ",{"type":26,"tag":42,"props":1453,"children":1455},{"href":1454},"https://docs.gitlab.com/runner/",[1456],{"type":39,"value":1457},"GitLab Runner",{"type":39,"value":1459}," takes care of running pipelines.",{"type":26,"tag":35,"props":1461,"children":1462},{},[1463],{"type":39,"value":1464},"Renovate automates dependency updates. PHP, Go, Python, Docker - to name a couple. We already use it for various projects.",{"type":26,"tag":64,"props":1466,"children":1468},{"id":1467},"container-with-docker-and-docker-compose",[1469],{"type":39,"value":1470},"Container with Docker and Docker Compose",{"type":26,"tag":35,"props":1472,"children":1473},{},[1474,1476,1482],{"type":39,"value":1475},"The main reason why we chose Docker is the ability to access a remote Docker host and execute Docker commands.\nRefer to ",{"type":26,"tag":42,"props":1477,"children":1479},{"href":1478},"https://docs.docker.com/reference/cli/docker/#host",[1480],{"type":39,"value":1481},"the official documentation",{"type":39,"value":1483}," for more information.",{"type":26,"tag":35,"props":1485,"children":1486},{},[1487,1489,1495],{"type":39,"value":1488},"We are using ",{"type":26,"tag":83,"props":1490,"children":1492},{"className":1491},[],[1493],{"type":39,"value":1494},"ssh",{"type":39,"value":1496}," to access our target server.",{"type":26,"tag":35,"props":1498,"children":1499},{},[1500],{"type":26,"tag":83,"props":1501,"children":1503},{"className":1502},[],[1504],{"type":39,"value":1505},"DOCKER_HOST=ssh://[username@]\u003CIP or host>[:port] docker compose up --wait",{"type":26,"tag":64,"props":1507,"children":1509},{"id":1508},"gitops-with-gitlab",[1510],{"type":39,"value":1511},"GitOps with GitLab",{"type":26,"tag":35,"props":1513,"children":1514},{},[1515],{"type":39,"value":1516},"The idea behind GitOps is to use a git repository to store the configuration. Here is an example:",{"type":26,"tag":76,"props":1518,"children":1520},{"className":78,"code":1519,"language":80,"meta":7,"style":7},".\n├──.gitlab-ci.yml             # pipeline definition\n├── renovate.json             # Renovate configuration\n├── nextcloud\n│   ├── docker-compose.yml   # Nextcloud file hosting and collaboration\n└── traefik\n    └── docker-compose.yml   # Traefik reverse proxy configuration\n\n",[1521],{"type":26,"tag":83,"props":1522,"children":1523},{"__ignoreMap":7},[1524,1533,1547,1565,1577,1600,1613],{"type":26,"tag":87,"props":1525,"children":1526},{"class":89,"line":90},[1527],{"type":26,"tag":87,"props":1528,"children":1530},{"style":1529},"--shiki-default:#79B8FF;--shiki-dark:#79B8FF;--shiki-sepia:#66D9EF",[1531],{"type":39,"value":1532},".\n",{"type":26,"tag":87,"props":1534,"children":1535},{"class":89,"line":242},[1536,1541],{"type":26,"tag":87,"props":1537,"children":1538},{"style":94},[1539],{"type":39,"value":1540},"├──.gitlab-ci.yml",{"type":26,"tag":87,"props":1542,"children":1544},{"style":1543},"--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#88846F",[1545],{"type":39,"value":1546},"             # pipeline definition\n",{"type":26,"tag":87,"props":1548,"children":1549},{"class":89,"line":256},[1550,1555,1560],{"type":26,"tag":87,"props":1551,"children":1552},{"style":94},[1553],{"type":39,"value":1554},"├──",{"type":26,"tag":87,"props":1556,"children":1557},{"style":99},[1558],{"type":39,"value":1559}," renovate.json",{"type":26,"tag":87,"props":1561,"children":1562},{"style":1543},[1563],{"type":39,"value":1564},"             # Renovate configuration\n",{"type":26,"tag":87,"props":1566,"children":1567},{"class":89,"line":266},[1568,1572],{"type":26,"tag":87,"props":1569,"children":1570},{"style":94},[1571],{"type":39,"value":1554},{"type":26,"tag":87,"props":1573,"children":1574},{"style":99},[1575],{"type":39,"value":1576}," nextcloud\n",{"type":26,"tag":87,"props":1578,"children":1579},{"class":89,"line":279},[1580,1585,1590,1595],{"type":26,"tag":87,"props":1581,"children":1582},{"style":94},[1583],{"type":39,"value":1584},"│",{"type":26,"tag":87,"props":1586,"children":1587},{"style":99},[1588],{"type":39,"value":1589},"   ├──",{"type":26,"tag":87,"props":1591,"children":1592},{"style":99},[1593],{"type":39,"value":1594}," docker-compose.yml",{"type":26,"tag":87,"props":1596,"children":1597},{"style":1543},[1598],{"type":39,"value":1599},"   # Nextcloud file hosting and collaboration\n",{"type":26,"tag":87,"props":1601,"children":1602},{"class":89,"line":214},[1603,1608],{"type":26,"tag":87,"props":1604,"children":1605},{"style":94},[1606],{"type":39,"value":1607},"└──",{"type":26,"tag":87,"props":1609,"children":1610},{"style":99},[1611],{"type":39,"value":1612}," traefik\n",{"type":26,"tag":87,"props":1614,"children":1615},{"class":89,"line":312},[1616,1621,1625],{"type":26,"tag":87,"props":1617,"children":1618},{"style":94},[1619],{"type":39,"value":1620},"    └──",{"type":26,"tag":87,"props":1622,"children":1623},{"style":99},[1624],{"type":39,"value":1594},{"type":26,"tag":87,"props":1626,"children":1627},{"style":1543},[1628],{"type":39,"value":1629},"   # Traefik reverse proxy configuration\n",{"type":26,"tag":76,"props":1631,"children":1634},{"className":210,"code":1632,"filename":1633,"language":220,"meta":7,"style":7},"volumes:\n  nextcloud:\n  db:\n\nservices:\n  db:\n    image: mariadb:11.8\n    restart: unless-stopped\n    volumes:\n      - db:/var/lib/mysql\n    environment:\n      - MARIADB_ROOT_PASSWORD=${NEXTCLOUD_MARIADB_ROOT_PASSWORD:?error}\n      - MARIADB_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n      - MARIADB_DATABASE=nextcloud\n      - MARIADB_USER=nextcloud\n    command:\n      - --transaction-isolation=READ-COMMITTED\n      - --log-bin=binlog\n      - --binlog-format=ROW\n    healthcheck:\n      test: [\"CMD\", \"healthcheck.sh\", \"--connect\", \"--innodb_initialized\"]\n      interval: 15s\n      timeout: 5s\n      retries: 6\n\n  nextcloud:\n    image: nextcloud:32.0.0\n    restart: unless-stopped\n    depends_on:\n      db:\n        condition: service_healthy\n    volumes:\n      - nextcloud:/var/www/html\n    environment:\n      - MYSQL_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n      - MYSQL_DATABASE=nextcloud\n      - MYSQL_USER=nextcloud\n      - MYSQL_HOST=db\n","nextcloud/docker-compose.yaml",[1635],{"type":26,"tag":83,"props":1636,"children":1637},{"__ignoreMap":7},[1638,1650,1662,1674,1681,1693,1704,1721,1738,1750,1762,1774,1786,1798,1810,1822,1834,1846,1858,1870,1882,1931,1948,1965,1982,1989,2000,2016,2031,2043,2055,2072,2083,2095,2106,2118,2130,2142],{"type":26,"tag":87,"props":1639,"children":1640},{"class":89,"line":90},[1641,1646],{"type":26,"tag":87,"props":1642,"children":1643},{"style":230},[1644],{"type":39,"value":1645},"volumes",{"type":26,"tag":87,"props":1647,"children":1648},{"style":236},[1649],{"type":39,"value":239},{"type":26,"tag":87,"props":1651,"children":1652},{"class":89,"line":242},[1653,1658],{"type":26,"tag":87,"props":1654,"children":1655},{"style":230},[1656],{"type":39,"value":1657},"  nextcloud",{"type":26,"tag":87,"props":1659,"children":1660},{"style":236},[1661],{"type":39,"value":239},{"type":26,"tag":87,"props":1663,"children":1664},{"class":89,"line":256},[1665,1670],{"type":26,"tag":87,"props":1666,"children":1667},{"style":230},[1668],{"type":39,"value":1669},"  db",{"type":26,"tag":87,"props":1671,"children":1672},{"style":236},[1673],{"type":39,"value":239},{"type":26,"tag":87,"props":1675,"children":1676},{"class":89,"line":266},[1677],{"type":26,"tag":87,"props":1678,"children":1679},{"emptyLinePlaceholder":260},[1680],{"type":39,"value":263},{"type":26,"tag":87,"props":1682,"children":1683},{"class":89,"line":279},[1684,1689],{"type":26,"tag":87,"props":1685,"children":1686},{"style":230},[1687],{"type":39,"value":1688},"services",{"type":26,"tag":87,"props":1690,"children":1691},{"style":236},[1692],{"type":39,"value":239},{"type":26,"tag":87,"props":1694,"children":1695},{"class":89,"line":214},[1696,1700],{"type":26,"tag":87,"props":1697,"children":1698},{"style":230},[1699],{"type":39,"value":1669},{"type":26,"tag":87,"props":1701,"children":1702},{"style":236},[1703],{"type":39,"value":239},{"type":26,"tag":87,"props":1705,"children":1706},{"class":89,"line":312},[1707,1712,1716],{"type":26,"tag":87,"props":1708,"children":1709},{"style":230},[1710],{"type":39,"value":1711},"    image",{"type":26,"tag":87,"props":1713,"children":1714},{"style":236},[1715],{"type":39,"value":304},{"type":26,"tag":87,"props":1717,"children":1718},{"style":99},[1719],{"type":39,"value":1720},"mariadb:11.8\n",{"type":26,"tag":87,"props":1722,"children":1723},{"class":89,"line":336},[1724,1729,1733],{"type":26,"tag":87,"props":1725,"children":1726},{"style":230},[1727],{"type":39,"value":1728},"    restart",{"type":26,"tag":87,"props":1730,"children":1731},{"style":236},[1732],{"type":39,"value":304},{"type":26,"tag":87,"props":1734,"children":1735},{"style":99},[1736],{"type":39,"value":1737},"unless-stopped\n",{"type":26,"tag":87,"props":1739,"children":1740},{"class":89,"line":353},[1741,1746],{"type":26,"tag":87,"props":1742,"children":1743},{"style":230},[1744],{"type":39,"value":1745},"    volumes",{"type":26,"tag":87,"props":1747,"children":1748},{"style":236},[1749],{"type":39,"value":239},{"type":26,"tag":87,"props":1751,"children":1752},{"class":89,"line":366},[1753,1757],{"type":26,"tag":87,"props":1754,"children":1755},{"style":236},[1756],{"type":39,"value":873},{"type":26,"tag":87,"props":1758,"children":1759},{"style":99},[1760],{"type":39,"value":1761},"db:/var/lib/mysql\n",{"type":26,"tag":87,"props":1763,"children":1764},{"class":89,"line":384},[1765,1770],{"type":26,"tag":87,"props":1766,"children":1767},{"style":230},[1768],{"type":39,"value":1769},"    environment",{"type":26,"tag":87,"props":1771,"children":1772},{"style":236},[1773],{"type":39,"value":239},{"type":26,"tag":87,"props":1775,"children":1776},{"class":89,"line":402},[1777,1781],{"type":26,"tag":87,"props":1778,"children":1779},{"style":236},[1780],{"type":39,"value":873},{"type":26,"tag":87,"props":1782,"children":1783},{"style":99},[1784],{"type":39,"value":1785},"MARIADB_ROOT_PASSWORD=${NEXTCLOUD_MARIADB_ROOT_PASSWORD:?error}\n",{"type":26,"tag":87,"props":1787,"children":1788},{"class":89,"line":415},[1789,1793],{"type":26,"tag":87,"props":1790,"children":1791},{"style":236},[1792],{"type":39,"value":873},{"type":26,"tag":87,"props":1794,"children":1795},{"style":99},[1796],{"type":39,"value":1797},"MARIADB_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n",{"type":26,"tag":87,"props":1799,"children":1800},{"class":89,"line":215},[1801,1805],{"type":26,"tag":87,"props":1802,"children":1803},{"style":236},[1804],{"type":39,"value":873},{"type":26,"tag":87,"props":1806,"children":1807},{"style":99},[1808],{"type":39,"value":1809},"MARIADB_DATABASE=nextcloud\n",{"type":26,"tag":87,"props":1811,"children":1812},{"class":89,"line":216},[1813,1817],{"type":26,"tag":87,"props":1814,"children":1815},{"style":236},[1816],{"type":39,"value":873},{"type":26,"tag":87,"props":1818,"children":1819},{"style":99},[1820],{"type":39,"value":1821},"MARIADB_USER=nextcloud\n",{"type":26,"tag":87,"props":1823,"children":1824},{"class":89,"line":217},[1825,1830],{"type":26,"tag":87,"props":1826,"children":1827},{"style":230},[1828],{"type":39,"value":1829},"    command",{"type":26,"tag":87,"props":1831,"children":1832},{"style":236},[1833],{"type":39,"value":239},{"type":26,"tag":87,"props":1835,"children":1836},{"class":89,"line":218},[1837,1841],{"type":26,"tag":87,"props":1838,"children":1839},{"style":236},[1840],{"type":39,"value":873},{"type":26,"tag":87,"props":1842,"children":1843},{"style":99},[1844],{"type":39,"value":1845},"--transaction-isolation=READ-COMMITTED\n",{"type":26,"tag":87,"props":1847,"children":1848},{"class":89,"line":219},[1849,1853],{"type":26,"tag":87,"props":1850,"children":1851},{"style":236},[1852],{"type":39,"value":873},{"type":26,"tag":87,"props":1854,"children":1855},{"style":99},[1856],{"type":39,"value":1857},"--log-bin=binlog\n",{"type":26,"tag":87,"props":1859,"children":1860},{"class":89,"line":503},[1861,1865],{"type":26,"tag":87,"props":1862,"children":1863},{"style":236},[1864],{"type":39,"value":873},{"type":26,"tag":87,"props":1866,"children":1867},{"style":99},[1868],{"type":39,"value":1869},"--binlog-format=ROW\n",{"type":26,"tag":87,"props":1871,"children":1872},{"class":89,"line":516},[1873,1878],{"type":26,"tag":87,"props":1874,"children":1875},{"style":230},[1876],{"type":39,"value":1877},"    healthcheck",{"type":26,"tag":87,"props":1879,"children":1880},{"style":236},[1881],{"type":39,"value":239},{"type":26,"tag":87,"props":1883,"children":1884},{"class":89,"line":538},[1885,1890,1894,1899,1904,1909,1913,1918,1922,1927],{"type":26,"tag":87,"props":1886,"children":1887},{"style":230},[1888],{"type":39,"value":1889},"      test",{"type":26,"tag":87,"props":1891,"children":1892},{"style":236},[1893],{"type":39,"value":323},{"type":26,"tag":87,"props":1895,"children":1896},{"style":99},[1897],{"type":39,"value":1898},"\"CMD\"",{"type":26,"tag":87,"props":1900,"children":1901},{"style":236},[1902],{"type":39,"value":1903},", ",{"type":26,"tag":87,"props":1905,"children":1906},{"style":99},[1907],{"type":39,"value":1908},"\"healthcheck.sh\"",{"type":26,"tag":87,"props":1910,"children":1911},{"style":236},[1912],{"type":39,"value":1903},{"type":26,"tag":87,"props":1914,"children":1915},{"style":99},[1916],{"type":39,"value":1917},"\"--connect\"",{"type":26,"tag":87,"props":1919,"children":1920},{"style":236},[1921],{"type":39,"value":1903},{"type":26,"tag":87,"props":1923,"children":1924},{"style":99},[1925],{"type":39,"value":1926},"\"--innodb_initialized\"",{"type":26,"tag":87,"props":1928,"children":1929},{"style":236},[1930],{"type":39,"value":333},{"type":26,"tag":87,"props":1932,"children":1933},{"class":89,"line":556},[1934,1939,1943],{"type":26,"tag":87,"props":1935,"children":1936},{"style":230},[1937],{"type":39,"value":1938},"      interval",{"type":26,"tag":87,"props":1940,"children":1941},{"style":236},[1942],{"type":39,"value":304},{"type":26,"tag":87,"props":1944,"children":1945},{"style":99},[1946],{"type":39,"value":1947},"15s\n",{"type":26,"tag":87,"props":1949,"children":1950},{"class":89,"line":596},[1951,1956,1960],{"type":26,"tag":87,"props":1952,"children":1953},{"style":230},[1954],{"type":39,"value":1955},"      timeout",{"type":26,"tag":87,"props":1957,"children":1958},{"style":236},[1959],{"type":39,"value":304},{"type":26,"tag":87,"props":1961,"children":1962},{"style":99},[1963],{"type":39,"value":1964},"5s\n",{"type":26,"tag":87,"props":1966,"children":1967},{"class":89,"line":894},[1968,1973,1977],{"type":26,"tag":87,"props":1969,"children":1970},{"style":230},[1971],{"type":39,"value":1972},"      retries",{"type":26,"tag":87,"props":1974,"children":1975},{"style":236},[1976],{"type":39,"value":304},{"type":26,"tag":87,"props":1978,"children":1979},{"style":1061},[1980],{"type":39,"value":1981},"6\n",{"type":26,"tag":87,"props":1983,"children":1984},{"class":89,"line":906},[1985],{"type":26,"tag":87,"props":1986,"children":1987},{"emptyLinePlaceholder":260},[1988],{"type":39,"value":263},{"type":26,"tag":87,"props":1990,"children":1991},{"class":89,"line":926},[1992,1996],{"type":26,"tag":87,"props":1993,"children":1994},{"style":230},[1995],{"type":39,"value":1657},{"type":26,"tag":87,"props":1997,"children":1998},{"style":236},[1999],{"type":39,"value":239},{"type":26,"tag":87,"props":2001,"children":2002},{"class":89,"line":942},[2003,2007,2011],{"type":26,"tag":87,"props":2004,"children":2005},{"style":230},[2006],{"type":39,"value":1711},{"type":26,"tag":87,"props":2008,"children":2009},{"style":236},[2010],{"type":39,"value":304},{"type":26,"tag":87,"props":2012,"children":2013},{"style":99},[2014],{"type":39,"value":2015},"nextcloud:32.0.0\n",{"type":26,"tag":87,"props":2017,"children":2018},{"class":89,"line":962},[2019,2023,2027],{"type":26,"tag":87,"props":2020,"children":2021},{"style":230},[2022],{"type":39,"value":1728},{"type":26,"tag":87,"props":2024,"children":2025},{"style":236},[2026],{"type":39,"value":304},{"type":26,"tag":87,"props":2028,"children":2029},{"style":99},[2030],{"type":39,"value":1737},{"type":26,"tag":87,"props":2032,"children":2033},{"class":89,"line":970},[2034,2039],{"type":26,"tag":87,"props":2035,"children":2036},{"style":230},[2037],{"type":39,"value":2038},"    depends_on",{"type":26,"tag":87,"props":2040,"children":2041},{"style":236},[2042],{"type":39,"value":239},{"type":26,"tag":87,"props":2044,"children":2045},{"class":89,"line":983},[2046,2051],{"type":26,"tag":87,"props":2047,"children":2048},{"style":230},[2049],{"type":39,"value":2050},"      db",{"type":26,"tag":87,"props":2052,"children":2053},{"style":236},[2054],{"type":39,"value":239},{"type":26,"tag":87,"props":2056,"children":2057},{"class":89,"line":999},[2058,2063,2067],{"type":26,"tag":87,"props":2059,"children":2060},{"style":230},[2061],{"type":39,"value":2062},"        condition",{"type":26,"tag":87,"props":2064,"children":2065},{"style":236},[2066],{"type":39,"value":304},{"type":26,"tag":87,"props":2068,"children":2069},{"style":99},[2070],{"type":39,"value":2071},"service_healthy\n",{"type":26,"tag":87,"props":2073,"children":2074},{"class":89,"line":1011},[2075,2079],{"type":26,"tag":87,"props":2076,"children":2077},{"style":230},[2078],{"type":39,"value":1745},{"type":26,"tag":87,"props":2080,"children":2081},{"style":236},[2082],{"type":39,"value":239},{"type":26,"tag":87,"props":2084,"children":2085},{"class":89,"line":1028},[2086,2090],{"type":26,"tag":87,"props":2087,"children":2088},{"style":236},[2089],{"type":39,"value":873},{"type":26,"tag":87,"props":2091,"children":2092},{"style":99},[2093],{"type":39,"value":2094},"nextcloud:/var/www/html\n",{"type":26,"tag":87,"props":2096,"children":2097},{"class":89,"line":1048},[2098,2102],{"type":26,"tag":87,"props":2099,"children":2100},{"style":230},[2101],{"type":39,"value":1769},{"type":26,"tag":87,"props":2103,"children":2104},{"style":236},[2105],{"type":39,"value":239},{"type":26,"tag":87,"props":2107,"children":2108},{"class":89,"line":1067},[2109,2113],{"type":26,"tag":87,"props":2110,"children":2111},{"style":236},[2112],{"type":39,"value":873},{"type":26,"tag":87,"props":2114,"children":2115},{"style":99},[2116],{"type":39,"value":2117},"MYSQL_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n",{"type":26,"tag":87,"props":2119,"children":2120},{"class":89,"line":1079},[2121,2125],{"type":26,"tag":87,"props":2122,"children":2123},{"style":236},[2124],{"type":39,"value":873},{"type":26,"tag":87,"props":2126,"children":2127},{"style":99},[2128],{"type":39,"value":2129},"MYSQL_DATABASE=nextcloud\n",{"type":26,"tag":87,"props":2131,"children":2132},{"class":89,"line":1092},[2133,2137],{"type":26,"tag":87,"props":2134,"children":2135},{"style":236},[2136],{"type":39,"value":873},{"type":26,"tag":87,"props":2138,"children":2139},{"style":99},[2140],{"type":39,"value":2141},"MYSQL_USER=nextcloud\n",{"type":26,"tag":87,"props":2143,"children":2144},{"class":89,"line":1104},[2145,2149],{"type":26,"tag":87,"props":2146,"children":2147},{"style":236},[2148],{"type":39,"value":873},{"type":26,"tag":87,"props":2150,"children":2151},{"style":99},[2152],{"type":39,"value":2153},"MYSQL_HOST=db\n",{"type":26,"tag":76,"props":2155,"children":2159},{"className":2156,"code":2157,"language":2158,"meta":7,"style":7},"language-dotenv shiki shiki-themes github-dark github-dark monokai","NEXTCLOUD_MARIADB_ROOT_PASSWORD=\nNEXTCLOUD_MARIADB_PASSWORD=\n","dotenv",[2160],{"type":26,"tag":83,"props":2161,"children":2162},{"__ignoreMap":7},[2163,2178],{"type":26,"tag":87,"props":2164,"children":2165},{"class":89,"line":90},[2166,2172],{"type":26,"tag":87,"props":2167,"children":2169},{"style":2168},"--shiki-default:#FFAB70;--shiki-dark:#FFAB70;--shiki-sepia:#F8F8F2",[2170],{"type":39,"value":2171},"NEXTCLOUD_MARIADB_ROOT_PASSWORD",{"type":26,"tag":87,"props":2173,"children":2175},{"style":2174},"--shiki-default:#F97583;--shiki-dark:#F97583;--shiki-sepia:#F92672",[2176],{"type":39,"value":2177},"=\n",{"type":26,"tag":87,"props":2179,"children":2180},{"class":89,"line":242},[2181,2186],{"type":26,"tag":87,"props":2182,"children":2183},{"style":2168},[2184],{"type":39,"value":2185},"NEXTCLOUD_MARIADB_PASSWORD",{"type":26,"tag":87,"props":2187,"children":2188},{"style":2174},[2189],{"type":39,"value":2177},{"type":26,"tag":35,"props":2191,"children":2192},{},[2193,2195],{"type":39,"value":2194},"are stored as ",{"type":26,"tag":42,"props":2196,"children":2198},{"href":2197},"https://docs.gitlab.com/ci/variables/",[2199],{"type":39,"value":2200},"CI/CD Variables",{"type":26,"tag":2202,"props":2203,"children":2204},"hr",{},[],{"type":26,"tag":35,"props":2206,"children":2207},{},[2208],{"type":39,"value":2209},"To deploy the stack, we have a pipeline:",{"type":26,"tag":76,"props":2211,"children":2214},{"className":210,"code":2212,"filename":2213,"language":220,"meta":7,"style":7},"stages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  image: docker:28\n  variables:\n    DOCKER_HOST: ssh://[username@]\u003CIP or host>[:port]\n  script:\n    - for file in $(find . -type f -name docker-compose.yml); do docker compose -f $file up --remove-orphans --wait; done\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n\n",".gitlab-ci.yml",[2215],{"type":26,"tag":83,"props":2216,"children":2217},{"__ignoreMap":7},[2218,2229,2241,2248,2260,2275,2291,2302,2319,2330,2342,2353],{"type":26,"tag":87,"props":2219,"children":2220},{"class":89,"line":90},[2221,2225],{"type":26,"tag":87,"props":2222,"children":2223},{"style":230},[2224],{"type":39,"value":233},{"type":26,"tag":87,"props":2226,"children":2227},{"style":236},[2228],{"type":39,"value":239},{"type":26,"tag":87,"props":2230,"children":2231},{"class":89,"line":242},[2232,2236],{"type":26,"tag":87,"props":2233,"children":2234},{"style":236},[2235],{"type":39,"value":248},{"type":26,"tag":87,"props":2237,"children":2238},{"style":99},[2239],{"type":39,"value":2240},"deploy\n",{"type":26,"tag":87,"props":2242,"children":2243},{"class":89,"line":256},[2244],{"type":26,"tag":87,"props":2245,"children":2246},{"emptyLinePlaceholder":260},[2247],{"type":39,"value":263},{"type":26,"tag":87,"props":2249,"children":2250},{"class":89,"line":266},[2251,2256],{"type":26,"tag":87,"props":2252,"children":2253},{"style":230},[2254],{"type":39,"value":2255},"deploy",{"type":26,"tag":87,"props":2257,"children":2258},{"style":236},[2259],{"type":39,"value":239},{"type":26,"tag":87,"props":2261,"children":2262},{"class":89,"line":279},[2263,2267,2271],{"type":26,"tag":87,"props":2264,"children":2265},{"style":230},[2266],{"type":39,"value":342},{"type":26,"tag":87,"props":2268,"children":2269},{"style":236},[2270],{"type":39,"value":304},{"type":26,"tag":87,"props":2272,"children":2273},{"style":99},[2274],{"type":39,"value":2240},{"type":26,"tag":87,"props":2276,"children":2277},{"class":89,"line":214},[2278,2282,2286],{"type":26,"tag":87,"props":2279,"children":2280},{"style":230},[2281],{"type":39,"value":285},{"type":26,"tag":87,"props":2283,"children":2284},{"style":236},[2285],{"type":39,"value":304},{"type":26,"tag":87,"props":2287,"children":2288},{"style":99},[2289],{"type":39,"value":2290},"docker:28\n",{"type":26,"tag":87,"props":2292,"children":2293},{"class":89,"line":312},[2294,2298],{"type":26,"tag":87,"props":2295,"children":2296},{"style":230},[2297],{"type":39,"value":359},{"type":26,"tag":87,"props":2299,"children":2300},{"style":236},[2301],{"type":39,"value":239},{"type":26,"tag":87,"props":2303,"children":2304},{"class":89,"line":336},[2305,2310,2314],{"type":26,"tag":87,"props":2306,"children":2307},{"style":230},[2308],{"type":39,"value":2309},"    DOCKER_HOST",{"type":26,"tag":87,"props":2311,"children":2312},{"style":236},[2313],{"type":39,"value":304},{"type":26,"tag":87,"props":2315,"children":2316},{"style":99},[2317],{"type":39,"value":2318},"ssh://[username@]\u003CIP or host>[:port]\n",{"type":26,"tag":87,"props":2320,"children":2321},{"class":89,"line":353},[2322,2326],{"type":26,"tag":87,"props":2323,"children":2324},{"style":230},[2325],{"type":39,"value":408},{"type":26,"tag":87,"props":2327,"children":2328},{"style":236},[2329],{"type":39,"value":239},{"type":26,"tag":87,"props":2331,"children":2332},{"class":89,"line":366},[2333,2337],{"type":26,"tag":87,"props":2334,"children":2335},{"style":236},[2336],{"type":39,"value":421},{"type":26,"tag":87,"props":2338,"children":2339},{"style":99},[2340],{"type":39,"value":2341},"for file in $(find . -type f -name docker-compose.yml); do docker compose -f $file up --remove-orphans --wait; done\n",{"type":26,"tag":87,"props":2343,"children":2344},{"class":89,"line":384},[2345,2349],{"type":26,"tag":87,"props":2346,"children":2347},{"style":230},[2348],{"type":39,"value":509},{"type":26,"tag":87,"props":2350,"children":2351},{"style":236},[2352],{"type":39,"value":239},{"type":26,"tag":87,"props":2354,"children":2355},{"class":89,"line":402},[2356,2360,2364,2368],{"type":26,"tag":87,"props":2357,"children":2358},{"style":236},[2359],{"type":39,"value":421},{"type":26,"tag":87,"props":2361,"children":2362},{"style":230},[2363],{"type":39,"value":526},{"type":26,"tag":87,"props":2365,"children":2366},{"style":236},[2367],{"type":39,"value":304},{"type":26,"tag":87,"props":2369,"children":2370},{"style":99},[2371],{"type":39,"value":2372},"$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n",{"type":26,"tag":35,"props":2374,"children":2375},{},[2376,2378,2384],{"type":39,"value":2377},"The pipeline runs with every commit on the default branch, iterates through all ",{"type":26,"tag":83,"props":2379,"children":2381},{"className":2380},[],[2382],{"type":39,"value":2383},"docker-compose.yml",{"type":39,"value":2385}," files, and deploys them.",{"type":26,"tag":64,"props":2387,"children":2389},{"id":2388},"keep-your-deployments-up-to-date-with-renovate-bot",[2390],{"type":39,"value":2391},"Keep your deployments up-to-date with Renovate Bot",{"type":26,"tag":35,"props":2393,"children":2394},{},[2395],{"type":39,"value":2396},"Here is where Renovate kicks in.",{"type":26,"tag":76,"props":2398,"children":2402},{"className":2399,"code":2400,"filename":2401,"language":1328,"meta":7,"style":7},"language-json shiki shiki-themes github-dark github-dark monokai","{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\"\n  ]\n}\n","renovate.json",[2403],{"type":26,"tag":83,"props":2404,"children":2405},{"__ignoreMap":7},[2406,2414,2438,2451,2459,2467],{"type":26,"tag":87,"props":2407,"children":2408},{"class":89,"line":90},[2409],{"type":26,"tag":87,"props":2410,"children":2411},{"style":236},[2412],{"type":39,"value":2413},"{\n",{"type":26,"tag":87,"props":2415,"children":2416},{"class":89,"line":242},[2417,2423,2427,2433],{"type":26,"tag":87,"props":2418,"children":2420},{"style":2419},"--shiki-default:#79B8FF;--shiki-default-font-style:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic",[2421],{"type":39,"value":2422},"  \"$schema\"",{"type":26,"tag":87,"props":2424,"children":2425},{"style":236},[2426],{"type":39,"value":304},{"type":26,"tag":87,"props":2428,"children":2430},{"style":2429},"--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF;--shiki-sepia:#CFCFC2",[2431],{"type":39,"value":2432},"\"https://docs.renovatebot.com/renovate-schema.json\"",{"type":26,"tag":87,"props":2434,"children":2435},{"style":236},[2436],{"type":39,"value":2437},",\n",{"type":26,"tag":87,"props":2439,"children":2440},{"class":89,"line":256},[2441,2446],{"type":26,"tag":87,"props":2442,"children":2443},{"style":2419},[2444],{"type":39,"value":2445},"  \"extends\"",{"type":26,"tag":87,"props":2447,"children":2448},{"style":236},[2449],{"type":39,"value":2450},": [\n",{"type":26,"tag":87,"props":2452,"children":2453},{"class":89,"line":266},[2454],{"type":26,"tag":87,"props":2455,"children":2456},{"style":2429},[2457],{"type":39,"value":2458},"    \"config:best-practices\"\n",{"type":26,"tag":87,"props":2460,"children":2461},{"class":89,"line":279},[2462],{"type":26,"tag":87,"props":2463,"children":2464},{"style":236},[2465],{"type":39,"value":2466},"  ]\n",{"type":26,"tag":87,"props":2468,"children":2469},{"class":89,"line":214},[2470],{"type":26,"tag":87,"props":2471,"children":2472},{"style":236},[2473],{"type":39,"value":2474},"}\n",{"type":26,"tag":35,"props":2476,"children":2477},{},[2478],{"type":39,"value":2479},"Renovate will create Merge Requests for every update. Nice!",{"type":26,"tag":64,"props":2481,"children":2483},{"id":2482},"automated-security-updates-and-opt-in-for-minormajor-version-updates",[2484],{"type":39,"value":2485},"Automated security updates and opt-in for minor/major version updates",{"type":26,"tag":35,"props":2487,"children":2488},{},[2489],{"type":39,"value":2490},"The current configuration creates Merge Requests for every update, but we want security updates to happen without user interaction.",{"type":26,"tag":35,"props":2492,"children":2493},{},[2494,2496],{"type":39,"value":2495},"You need to understand how Docker images are versioned / tagged. It depends on the image, but let's take a look on the official ",{"type":26,"tag":42,"props":2497,"children":2499},{"href":2498},"https://hub.docker.com/_/mariadb",[2500],{"type":39,"value":2501},"MariaDB",{"type":26,"tag":35,"props":2503,"children":2504},{},[2505,2507,2513],{"type":39,"value":2506},"There are ",{"type":26,"tag":83,"props":2508,"children":2510},{"className":2509},[],[2511],{"type":39,"value":2512},"11.8.3-noble, 11.8-noble, 11-noble, lts-noble, 11.8.3, 11.8, 11, lts",{"type":39,"value":2514},", all of which refer to the same image.",{"type":26,"tag":35,"props":2516,"children":2517},{},[2518,2524,2526,2532,2534,2540,2541,2547],{"type":26,"tag":83,"props":2519,"children":2521},{"className":2520},[],[2522],{"type":39,"value":2523},"11.8.3-noble",{"type":39,"value":2525}," means that we get MariaDB in version ",{"type":26,"tag":83,"props":2527,"children":2529},{"className":2528},[],[2530],{"type":39,"value":2531},"11.8.3",{"type":39,"value":2533}," based on Ubuntu Noble.\n",{"type":26,"tag":83,"props":2535,"children":2537},{"className":2536},[],[2538],{"type":39,"value":2539},"11.8-noble",{"type":39,"value":2525},{"type":26,"tag":83,"props":2542,"children":2544},{"className":2543},[],[2545],{"type":39,"value":2546},"11.8.\u003Clatest_path>",{"type":39,"value":2548}," based on Ubuntu Noble.",{"type":26,"tag":35,"props":2550,"children":2551},{},[2552,2554,2560,2562,2567,2569,2574],{"type":39,"value":2553},"When a neu version of MariaDB is released, e.g ",{"type":26,"tag":83,"props":2555,"children":2557},{"className":2556},[],[2558],{"type":39,"value":2559},"11.8.4-noble",{"type":39,"value":2561},", a new ",{"type":26,"tag":83,"props":2563,"children":2565},{"className":2564},[],[2566],{"type":39,"value":2559},{"type":39,"value":2568}," tag will be pushed, but the ",{"type":26,"tag":83,"props":2570,"children":2572},{"className":2571},[],[2573],{"type":39,"value":2539},{"type":39,"value":2575}," will be updated.",{"type":26,"tag":35,"props":2577,"children":2578},{},[2579,2581,2586],{"type":39,"value":2580},"This same is true for the Ubuntu update. The ",{"type":26,"tag":83,"props":2582,"children":2584},{"className":2583},[],[2585],{"type":39,"value":2523},{"type":39,"value":2587}," tag can be updated, if the image is rebuild with the latest Ubuntu image.",{"type":26,"tag":35,"props":2589,"children":2590},{},[2591,2593,2599,2601,2607],{"type":39,"value":2592},"Running ",{"type":26,"tag":83,"props":2594,"children":2596},{"className":2595},[],[2597],{"type":39,"value":2598},"docker compose up",{"type":39,"value":2600}," on ",{"type":26,"tag":83,"props":2602,"children":2604},{"className":2603},[],[2605],{"type":39,"value":2606},"mariadb:11.8-noble",{"type":39,"value":2608}," will do nothing, because Docker is not aware of that change.",{"type":26,"tag":35,"props":2610,"children":2611},{},[2612,2614,2620],{"type":39,"value":2613},"In the example above we reference ",{"type":26,"tag":83,"props":2615,"children":2617},{"className":2616},[],[2618],{"type":39,"value":2619},"mariadb:11.8",{"type":39,"value":2621},", because we want to use the latest patch version based on the most current OS.",{"type":26,"tag":2202,"props":2623,"children":2624},{},[],{"type":26,"tag":35,"props":2626,"children":2627},{},[2628],{"type":39,"value":2629},"How to tell Docker that there is a new version?",{"type":26,"tag":35,"props":2631,"children":2632},{},[2633,2635,2641],{"type":39,"value":2634},"The main idea is to attach a ",{"type":26,"tag":42,"props":2636,"children":2638},{"href":2637},"https://docs.docker.com/dhi/core-concepts/digests/",[2639],{"type":39,"value":2640},"digest",{"type":39,"value":2642}," to the Docker image.",{"type":26,"tag":35,"props":2644,"children":2645},{},[2646,2648,2653,2655],{"type":39,"value":2647},"When running Renovate for the first time, it will find the reference to ",{"type":26,"tag":83,"props":2649,"children":2651},{"className":2650},[],[2652],{"type":39,"value":2619},{"type":39,"value":2654}," and create a merge request to pin the Digest to something like ",{"type":26,"tag":83,"props":2656,"children":2658},{"className":2657},[],[2659],{"type":39,"value":2660},"mariadb:11.8@sha256:ae6119716edac6998ae85508431b3d2e666530ddf4e94c61a10710caec9b0f71",{"type":26,"tag":35,"props":2662,"children":2663},{},[2664],{"type":39,"value":2665},"Renovate will also monitor the upstream changes, so that every time the image get updated, the digest will change and Renovate will create a merge request.",{"type":26,"tag":35,"props":2667,"children":2668},{},[2669],{"type":39,"value":2670},"To merge these updates automatically, we need to do some adjustments.",{"type":26,"tag":76,"props":2672,"children":2675},{"className":2399,"code":2673,"filename":2401,"highlights":2674,"language":1328,"meta":7,"style":7},"{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\",\n    \"default:automergeDigest\"\n  ],\n  \"automergeType\": \"branch\",\n  \"ignoreTests\": true\n}\n",[279,214,312,336],[2676],{"type":26,"tag":83,"props":2677,"children":2678},{"__ignoreMap":7},[2679,2686,2705,2716,2728,2737,2746,2768,2785],{"type":26,"tag":87,"props":2680,"children":2681},{"class":89,"line":90},[2682],{"type":26,"tag":87,"props":2683,"children":2684},{"style":236},[2685],{"type":39,"value":2413},{"type":26,"tag":87,"props":2687,"children":2688},{"class":89,"line":242},[2689,2693,2697,2701],{"type":26,"tag":87,"props":2690,"children":2691},{"style":2419},[2692],{"type":39,"value":2422},{"type":26,"tag":87,"props":2694,"children":2695},{"style":236},[2696],{"type":39,"value":304},{"type":26,"tag":87,"props":2698,"children":2699},{"style":2429},[2700],{"type":39,"value":2432},{"type":26,"tag":87,"props":2702,"children":2703},{"style":236},[2704],{"type":39,"value":2437},{"type":26,"tag":87,"props":2706,"children":2707},{"class":89,"line":256},[2708,2712],{"type":26,"tag":87,"props":2709,"children":2710},{"style":2419},[2711],{"type":39,"value":2445},{"type":26,"tag":87,"props":2713,"children":2714},{"style":236},[2715],{"type":39,"value":2450},{"type":26,"tag":87,"props":2717,"children":2718},{"class":89,"line":266},[2719,2724],{"type":26,"tag":87,"props":2720,"children":2721},{"style":2429},[2722],{"type":39,"value":2723},"    \"config:best-practices\"",{"type":26,"tag":87,"props":2725,"children":2726},{"style":236},[2727],{"type":39,"value":2437},{"type":26,"tag":87,"props":2729,"children":2731},{"class":2730,"line":279},[89,293],[2732],{"type":26,"tag":87,"props":2733,"children":2734},{"style":2429},[2735],{"type":39,"value":2736},"    \"default:automergeDigest\"\n",{"type":26,"tag":87,"props":2738,"children":2740},{"class":2739,"line":214},[89,293],[2741],{"type":26,"tag":87,"props":2742,"children":2743},{"style":236},[2744],{"type":39,"value":2745},"  ],\n",{"type":26,"tag":87,"props":2747,"children":2749},{"class":2748,"line":312},[89,293],[2750,2755,2759,2764],{"type":26,"tag":87,"props":2751,"children":2752},{"style":2419},[2753],{"type":39,"value":2754},"  \"automergeType\"",{"type":26,"tag":87,"props":2756,"children":2757},{"style":236},[2758],{"type":39,"value":304},{"type":26,"tag":87,"props":2760,"children":2761},{"style":2429},[2762],{"type":39,"value":2763},"\"branch\"",{"type":26,"tag":87,"props":2765,"children":2766},{"style":236},[2767],{"type":39,"value":2437},{"type":26,"tag":87,"props":2769,"children":2771},{"class":2770,"line":336},[89,293],[2772,2777,2781],{"type":26,"tag":87,"props":2773,"children":2774},{"style":2419},[2775],{"type":39,"value":2776},"  \"ignoreTests\"",{"type":26,"tag":87,"props":2778,"children":2779},{"style":236},[2780],{"type":39,"value":304},{"type":26,"tag":87,"props":2782,"children":2783},{"style":1061},[2784],{"type":39,"value":1064},{"type":26,"tag":87,"props":2786,"children":2787},{"class":89,"line":353},[2788],{"type":26,"tag":87,"props":2789,"children":2790},{"style":236},[2791],{"type":39,"value":2474},{"type":26,"tag":35,"props":2793,"children":2794},{},[2795,2797,2803,2804],{"type":39,"value":2796},"This instructs Renovate to auto-merge the Digest updates without creating a merge request prior. This reduces noise since there is no merge request notification.\nYou can read more about ",{"type":26,"tag":42,"props":2798,"children":2800},{"href":2799},"https://docs.renovatebot.com/key-concepts/automerge/#branch-vs-pr-automerging",[2801],{"type":39,"value":2802},"automergeType",{"type":39,"value":1398},{"type":26,"tag":42,"props":2805,"children":2807},{"href":2806},"https://docs.renovatebot.com/key-concepts/automerge/#absence-of-tests",[2808],{"type":39,"value":2809},"ignoreTests",{"type":26,"tag":1193,"props":2811,"children":2812},{},[2813],{"type":39,"value":1197},{"title":7,"searchDepth":242,"depth":242,"links":2815},[2816,2817,2818,2819,2820],{"id":1430,"depth":242,"text":1433},{"id":1467,"depth":242,"text":1470},{"id":1508,"depth":242,"text":1511},{"id":2388,"depth":242,"text":2391},{"id":2482,"depth":242,"text":2485},{"_path":1220,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1221,"description":1222,"author":10,"image":11,"releaseDate":1223,"blogCategories":2822,"articleTags":2823,"tags":2824,"body":2825,"_type":1202,"_id":1227,"_source":1204,"_file":1228,"_stem":1229,"_extension":1207},[14,15],[15],[21],{"type":23,"children":2826,"toc":3966},[2827,2838,2843,2968,2973,3046,3052,3078,3083,3107,3120,3138,3144,3161,3262,3267,3272,3297,3343,3346,3351,3692,3697,3733,3740,3748,3799,3803,3810,3838,3844,3849,3855,3860,3872,3880,3886,3898,3962],{"type":26,"tag":35,"props":2828,"children":2829},{},[2830,2836],{"type":26,"tag":42,"props":2831,"children":2833},{"href":2832},"https://doc.traefik.io/traefik/",[2834],{"type":39,"value":2835},"Traefik",{"type":39,"value":2837}," is a reverse proxy with excellent docker integration. It uses labeln attached to containers to route traffic to them.",{"type":26,"tag":35,"props":2839,"children":2840},{},[2841],{"type":39,"value":2842},"A common label set looks similar to this:",{"type":26,"tag":76,"props":2844,"children":2848},{"code":2845,"filename":2846,"highlights":2847,"language":220,"meta":7,"className":210,"style":7},"services:\n  whoami:\n    image: traefik/whoami\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.whoami.rule=Host(`whoami.example.com`)\"\n      - \"traefik.http.routers.whoami.entrypoints=websecure\"\n      - \"traefik.http.routers.whoami.tls=true\"\n      - \"traefik.http.routers.whoami.tls.certresolver=letsencrypt\"\n","whoami/docker-compose.yaml",[279,214,312,336,353],[2849],{"type":26,"tag":83,"props":2850,"children":2851},{"__ignoreMap":7},[2852,2863,2875,2891,2903,2916,2929,2942,2955],{"type":26,"tag":87,"props":2853,"children":2854},{"class":89,"line":90},[2855,2859],{"type":26,"tag":87,"props":2856,"children":2857},{"style":230},[2858],{"type":39,"value":1688},{"type":26,"tag":87,"props":2860,"children":2861},{"style":236},[2862],{"type":39,"value":239},{"type":26,"tag":87,"props":2864,"children":2865},{"class":89,"line":242},[2866,2871],{"type":26,"tag":87,"props":2867,"children":2868},{"style":230},[2869],{"type":39,"value":2870},"  whoami",{"type":26,"tag":87,"props":2872,"children":2873},{"style":236},[2874],{"type":39,"value":239},{"type":26,"tag":87,"props":2876,"children":2877},{"class":89,"line":256},[2878,2882,2886],{"type":26,"tag":87,"props":2879,"children":2880},{"style":230},[2881],{"type":39,"value":1711},{"type":26,"tag":87,"props":2883,"children":2884},{"style":236},[2885],{"type":39,"value":304},{"type":26,"tag":87,"props":2887,"children":2888},{"style":99},[2889],{"type":39,"value":2890},"traefik/whoami\n",{"type":26,"tag":87,"props":2892,"children":2893},{"class":89,"line":266},[2894,2899],{"type":26,"tag":87,"props":2895,"children":2896},{"style":230},[2897],{"type":39,"value":2898},"    labels",{"type":26,"tag":87,"props":2900,"children":2901},{"style":236},[2902],{"type":39,"value":239},{"type":26,"tag":87,"props":2904,"children":2906},{"class":2905,"line":279},[89,293],[2907,2911],{"type":26,"tag":87,"props":2908,"children":2909},{"style":236},[2910],{"type":39,"value":873},{"type":26,"tag":87,"props":2912,"children":2913},{"style":99},[2914],{"type":39,"value":2915},"\"traefik.enable=true\"\n",{"type":26,"tag":87,"props":2917,"children":2919},{"class":2918,"line":214},[89,293],[2920,2924],{"type":26,"tag":87,"props":2921,"children":2922},{"style":236},[2923],{"type":39,"value":873},{"type":26,"tag":87,"props":2925,"children":2926},{"style":99},[2927],{"type":39,"value":2928},"\"traefik.http.routers.whoami.rule=Host(`whoami.example.com`)\"\n",{"type":26,"tag":87,"props":2930,"children":2932},{"class":2931,"line":312},[89,293],[2933,2937],{"type":26,"tag":87,"props":2934,"children":2935},{"style":236},[2936],{"type":39,"value":873},{"type":26,"tag":87,"props":2938,"children":2939},{"style":99},[2940],{"type":39,"value":2941},"\"traefik.http.routers.whoami.entrypoints=websecure\"\n",{"type":26,"tag":87,"props":2943,"children":2945},{"class":2944,"line":336},[89,293],[2946,2950],{"type":26,"tag":87,"props":2947,"children":2948},{"style":236},[2949],{"type":39,"value":873},{"type":26,"tag":87,"props":2951,"children":2952},{"style":99},[2953],{"type":39,"value":2954},"\"traefik.http.routers.whoami.tls=true\"\n",{"type":26,"tag":87,"props":2956,"children":2958},{"class":2957,"line":353},[89,293],[2959,2963],{"type":26,"tag":87,"props":2960,"children":2961},{"style":236},[2962],{"type":39,"value":873},{"type":26,"tag":87,"props":2964,"children":2965},{"style":99},[2966],{"type":39,"value":2967},"\"traefik.http.routers.whoami.tls.certresolver=letsencrypt\"\n",{"type":26,"tag":35,"props":2969,"children":2970},{},[2971],{"type":39,"value":2972},"In this example:",{"type":26,"tag":124,"props":2974,"children":2975},{},[2976,2987,3003,3008,3020,3025],{"type":26,"tag":128,"props":2977,"children":2978},{},[2979,2985],{"type":26,"tag":83,"props":2980,"children":2982},{"className":2981},[],[2983],{"type":39,"value":2984},"whoami",{"type":39,"value":2986}," the name of the \"main\" service (2)",{"type":26,"tag":128,"props":2988,"children":2989},{},[2990,2995,2997],{"type":26,"tag":83,"props":2991,"children":2993},{"className":2992},[],[2994],{"type":39,"value":2984},{"type":39,"value":2996}," the also the default ",{"type":26,"tag":42,"props":2998,"children":3000},{"href":2999},"https://docs.docker.com/compose/how-tos/project-name/",[3001],{"type":39,"value":3002},"compose project name",{"type":26,"tag":128,"props":3004,"children":3005},{},[3006],{"type":39,"value":3007},"Traefik is activated (5)",{"type":26,"tag":128,"props":3009,"children":3010},{},[3011,3013,3018],{"type":39,"value":3012},"It is exposed under ",{"type":26,"tag":83,"props":3014,"children":3016},{"className":3015},[],[3017],{"type":39,"value":2984},{"type":39,"value":3019}," subdomain (6)",{"type":26,"tag":128,"props":3021,"children":3022},{},[3023],{"type":39,"value":3024},"It is served over https (7)",{"type":26,"tag":128,"props":3026,"children":3027},{},[3028,3030,3036,3038,3044],{"type":39,"value":3029},"A preconfigured certresolver named ",{"type":26,"tag":83,"props":3031,"children":3033},{"className":3032},[],[3034],{"type":39,"value":3035},"letsencrypt",{"type":39,"value":3037}," is used for ",{"type":26,"tag":83,"props":3039,"children":3041},{"className":3040},[],[3042],{"type":39,"value":3043},"tls",{"type":39,"value":3045}," (8-9)",{"type":26,"tag":64,"props":3047,"children":3049},{"id":3048},"the-problem",[3050],{"type":39,"value":3051},"The problem",{"type":26,"tag":35,"props":3053,"children":3054},{},[3055,3056,3062,3064,3070,3072,3077],{"type":39,"value":40},{"type":26,"tag":42,"props":3057,"children":3059},{"href":3058},"/blog/gitops-docker-renovate",[3060],{"type":39,"value":3061},"other post about gitops with docker",{"type":39,"value":3063}," I introduced a concept of using ",{"type":26,"tag":83,"props":3065,"children":3067},{"className":3066},[],[3068],{"type":39,"value":3069},"git",{"type":39,"value":3071},"\nas the sources of truth for docker deployments using ",{"type":26,"tag":42,"props":3073,"children":3074},{"href":1401},[3075],{"type":39,"value":3076},"docker compose",{"type":39,"value":586},{"type":26,"tag":35,"props":3079,"children":3080},{},[3081],{"type":39,"value":3082},"Now there are some requirements to this approach:",{"type":26,"tag":124,"props":3084,"children":3085},{},[3086,3097],{"type":26,"tag":128,"props":3087,"children":3088},{},[3089,3091],{"type":39,"value":3090},"every stack is exposer under ",{"type":26,"tag":83,"props":3092,"children":3094},{"className":3093},[],[3095],{"type":39,"value":3096},"\u003Cstack_name>.\u003Cyour_domain>",{"type":26,"tag":128,"props":3098,"children":3099},{},[3100,3102],{"type":39,"value":3101},"every stack is protected with ",{"type":26,"tag":83,"props":3103,"children":3105},{"className":3104},[],[3106],{"type":39,"value":3043},{"type":26,"tag":35,"props":3108,"children":3109},{},[3110,3112,3118],{"type":39,"value":3111},"We could of course edit every ",{"type":26,"tag":83,"props":3113,"children":3115},{"className":3114},[],[3116],{"type":39,"value":3117},"docker-compose.yaml",{"type":39,"value":3119}," file and add the required labels, but it quickly becomes obvious,\nthat all the labels are the same!",{"type":26,"tag":1174,"props":3121,"children":3122},{},[3123,3128,3133],{"type":26,"tag":128,"props":3124,"children":3125},{},[3126],{"type":39,"value":3127},"Enable Traefik",{"type":26,"tag":128,"props":3129,"children":3130},{},[3131],{"type":39,"value":3132},"Assign a subdomain",{"type":26,"tag":128,"props":3134,"children":3135},{},[3136],{"type":39,"value":3137},"Enable HTTPS",{"type":26,"tag":64,"props":3139,"children":3141},{"id":3140},"the-solution",[3142],{"type":39,"value":3143},"The solution",{"type":26,"tag":35,"props":3145,"children":3146},{},[3147,3149,3159],{"type":39,"value":3148},"By using ",{"type":26,"tag":42,"props":3150,"children":3152},{"href":3151},"https://docs.docker.com/compose/how-tos/environment-variables/envvars/#compose_project_name",[3153],{"type":26,"tag":83,"props":3154,"children":3156},{"className":3155},[],[3157],{"type":39,"value":3158},"$COMPOSE_PROJECT_NAME",{"type":39,"value":3160},"\nvariable we can create a more generic template.\nThis ensures that the Traefik rules are consistent.",{"type":26,"tag":76,"props":3162,"children":3165},{"code":3163,"filename":3164,"language":220,"meta":7,"className":210,"style":7},"services:\n  \u003Cmain_service>:\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.rule=Host(`\u003Cstack_name>.example.com`)\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.entrypoints=websecure\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls=true\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls.certresolver=letsencrypt\"\n","\u003Cstack_name>/docker-compose.yaml",[3166],{"type":26,"tag":83,"props":3167,"children":3168},{"__ignoreMap":7},[3169,3180,3192,3203,3214,3226,3238,3250],{"type":26,"tag":87,"props":3170,"children":3171},{"class":89,"line":90},[3172,3176],{"type":26,"tag":87,"props":3173,"children":3174},{"style":230},[3175],{"type":39,"value":1688},{"type":26,"tag":87,"props":3177,"children":3178},{"style":236},[3179],{"type":39,"value":239},{"type":26,"tag":87,"props":3181,"children":3182},{"class":89,"line":242},[3183,3188],{"type":26,"tag":87,"props":3184,"children":3185},{"style":230},[3186],{"type":39,"value":3187},"  \u003Cmain_service>",{"type":26,"tag":87,"props":3189,"children":3190},{"style":236},[3191],{"type":39,"value":239},{"type":26,"tag":87,"props":3193,"children":3194},{"class":89,"line":256},[3195,3199],{"type":26,"tag":87,"props":3196,"children":3197},{"style":230},[3198],{"type":39,"value":2898},{"type":26,"tag":87,"props":3200,"children":3201},{"style":236},[3202],{"type":39,"value":239},{"type":26,"tag":87,"props":3204,"children":3205},{"class":89,"line":266},[3206,3210],{"type":26,"tag":87,"props":3207,"children":3208},{"style":236},[3209],{"type":39,"value":873},{"type":26,"tag":87,"props":3211,"children":3212},{"style":99},[3213],{"type":39,"value":2915},{"type":26,"tag":87,"props":3215,"children":3216},{"class":89,"line":279},[3217,3221],{"type":26,"tag":87,"props":3218,"children":3219},{"style":236},[3220],{"type":39,"value":873},{"type":26,"tag":87,"props":3222,"children":3223},{"style":99},[3224],{"type":39,"value":3225},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.rule=Host(`\u003Cstack_name>.example.com`)\"\n",{"type":26,"tag":87,"props":3227,"children":3228},{"class":89,"line":214},[3229,3233],{"type":26,"tag":87,"props":3230,"children":3231},{"style":236},[3232],{"type":39,"value":873},{"type":26,"tag":87,"props":3234,"children":3235},{"style":99},[3236],{"type":39,"value":3237},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.entrypoints=websecure\"\n",{"type":26,"tag":87,"props":3239,"children":3240},{"class":89,"line":312},[3241,3245],{"type":26,"tag":87,"props":3242,"children":3243},{"style":236},[3244],{"type":39,"value":873},{"type":26,"tag":87,"props":3246,"children":3247},{"style":99},[3248],{"type":39,"value":3249},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls=true\"\n",{"type":26,"tag":87,"props":3251,"children":3252},{"class":89,"line":336},[3253,3257],{"type":26,"tag":87,"props":3254,"children":3255},{"style":236},[3256],{"type":39,"value":873},{"type":26,"tag":87,"props":3258,"children":3259},{"style":99},[3260],{"type":39,"value":3261},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls.certresolver=letsencrypt\"\n",{"type":26,"tag":35,"props":3263,"children":3264},{},[3265],{"type":39,"value":3266},"The good news is that Traefik lets us configure some defaults that will cover the above boilerplate!",{"type":26,"tag":35,"props":3268,"children":3269},{},[3270],{"type":39,"value":3271},"Let's us define two simple conventions:",{"type":26,"tag":1174,"props":3273,"children":3274},{},[3275,3286],{"type":26,"tag":128,"props":3276,"children":3277},{},[3278,3284],{"type":26,"tag":83,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":39,"value":3283},"app",{"type":39,"value":3285}," is the main container, where Traefik will route the traffic",{"type":26,"tag":128,"props":3287,"children":3288},{},[3289,3295],{"type":26,"tag":83,"props":3290,"children":3292},{"className":3291},[],[3293],{"type":39,"value":3294},"\u003Cstack_name>",{"type":39,"value":3296}," (the compose project name) is the subdomain",{"type":26,"tag":76,"props":3298,"children":3301},{"code":3299,"filename":2846,"language":220,"meta":3300,"className":210,"style":7},"services:\n  app:\n    image: traefik/whoami\n","(1)",[3302],{"type":26,"tag":83,"props":3303,"children":3304},{"__ignoreMap":7},[3305,3316,3328],{"type":26,"tag":87,"props":3306,"children":3307},{"class":89,"line":90},[3308,3312],{"type":26,"tag":87,"props":3309,"children":3310},{"style":230},[3311],{"type":39,"value":1688},{"type":26,"tag":87,"props":3313,"children":3314},{"style":236},[3315],{"type":39,"value":239},{"type":26,"tag":87,"props":3317,"children":3318},{"class":89,"line":242},[3319,3324],{"type":26,"tag":87,"props":3320,"children":3321},{"style":230},[3322],{"type":39,"value":3323},"  app",{"type":26,"tag":87,"props":3325,"children":3326},{"style":236},[3327],{"type":39,"value":239},{"type":26,"tag":87,"props":3329,"children":3330},{"class":89,"line":256},[3331,3335,3339],{"type":26,"tag":87,"props":3332,"children":3333},{"style":230},[3334],{"type":39,"value":1711},{"type":26,"tag":87,"props":3336,"children":3337},{"style":236},[3338],{"type":39,"value":304},{"type":26,"tag":87,"props":3340,"children":3341},{"style":99},[3342],{"type":39,"value":2890},{"type":26,"tag":2202,"props":3344,"children":3345},{},[],{"type":26,"tag":35,"props":3347,"children":3348},{},[3349],{"type":39,"value":3350},"Now let's configure traefik to do \"the magic\"",{"type":26,"tag":76,"props":3352,"children":3356},{"code":3353,"filename":3354,"language":220,"meta":3355,"className":210,"style":7},"volumes:\n  letsencrypt:\n    \nservices:\n  traefik:\n    container_name: traefik\n    restart: always\n    image: traefik:3\n    network_mode: host\n    command:\n      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true\n      - --certificatesresolvers.letsencrypt.acme.email=\u003Cyour_email_here>\n      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\n      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\n\n      - --entrypoints.web.address=:80\n      - --entrypoints.websecure.address=:443\n      \n      - --entrypoints.web.http.redirections.entrypoint.to=websecure\n      - --entrypoints.web.http.redirections.entrypoint.scheme=https\n      - --entrypoints.websecure.http.tls.certresolver=letsencrypt\n\n      - --providers.docker\n      - --providers.docker.defaultrule=Host(`{{ trimPrefix `app-` .Name }}.example.com`)\n      - --providers.docker.constraints=Label(`com.docker.compose.service`,`app`)\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n      - /letsencrypt:/letsencrypt\n","traefik/docker-compose.yaml","(2)",[3357],{"type":26,"tag":83,"props":3358,"children":3359},{"__ignoreMap":7},[3360,3371,3383,3391,3402,3414,3431,3447,3463,3480,3491,3503,3515,3527,3539,3546,3558,3570,3578,3590,3602,3614,3621,3633,3645,3657,3668,3680],{"type":26,"tag":87,"props":3361,"children":3362},{"class":89,"line":90},[3363,3367],{"type":26,"tag":87,"props":3364,"children":3365},{"style":230},[3366],{"type":39,"value":1645},{"type":26,"tag":87,"props":3368,"children":3369},{"style":236},[3370],{"type":39,"value":239},{"type":26,"tag":87,"props":3372,"children":3373},{"class":89,"line":242},[3374,3379],{"type":26,"tag":87,"props":3375,"children":3376},{"style":230},[3377],{"type":39,"value":3378},"  letsencrypt",{"type":26,"tag":87,"props":3380,"children":3381},{"style":236},[3382],{"type":39,"value":239},{"type":26,"tag":87,"props":3384,"children":3385},{"class":89,"line":256},[3386],{"type":26,"tag":87,"props":3387,"children":3388},{"style":236},[3389],{"type":39,"value":3390},"    \n",{"type":26,"tag":87,"props":3392,"children":3393},{"class":89,"line":266},[3394,3398],{"type":26,"tag":87,"props":3395,"children":3396},{"style":230},[3397],{"type":39,"value":1688},{"type":26,"tag":87,"props":3399,"children":3400},{"style":236},[3401],{"type":39,"value":239},{"type":26,"tag":87,"props":3403,"children":3404},{"class":89,"line":279},[3405,3410],{"type":26,"tag":87,"props":3406,"children":3407},{"style":230},[3408],{"type":39,"value":3409},"  traefik",{"type":26,"tag":87,"props":3411,"children":3412},{"style":236},[3413],{"type":39,"value":239},{"type":26,"tag":87,"props":3415,"children":3416},{"class":89,"line":214},[3417,3422,3426],{"type":26,"tag":87,"props":3418,"children":3419},{"style":230},[3420],{"type":39,"value":3421},"    container_name",{"type":26,"tag":87,"props":3423,"children":3424},{"style":236},[3425],{"type":39,"value":304},{"type":26,"tag":87,"props":3427,"children":3428},{"style":99},[3429],{"type":39,"value":3430},"traefik\n",{"type":26,"tag":87,"props":3432,"children":3433},{"class":89,"line":312},[3434,3438,3442],{"type":26,"tag":87,"props":3435,"children":3436},{"style":230},[3437],{"type":39,"value":1728},{"type":26,"tag":87,"props":3439,"children":3440},{"style":236},[3441],{"type":39,"value":304},{"type":26,"tag":87,"props":3443,"children":3444},{"style":99},[3445],{"type":39,"value":3446},"always\n",{"type":26,"tag":87,"props":3448,"children":3449},{"class":89,"line":336},[3450,3454,3458],{"type":26,"tag":87,"props":3451,"children":3452},{"style":230},[3453],{"type":39,"value":1711},{"type":26,"tag":87,"props":3455,"children":3456},{"style":236},[3457],{"type":39,"value":304},{"type":26,"tag":87,"props":3459,"children":3460},{"style":99},[3461],{"type":39,"value":3462},"traefik:3\n",{"type":26,"tag":87,"props":3464,"children":3465},{"class":89,"line":353},[3466,3471,3475],{"type":26,"tag":87,"props":3467,"children":3468},{"style":230},[3469],{"type":39,"value":3470},"    network_mode",{"type":26,"tag":87,"props":3472,"children":3473},{"style":236},[3474],{"type":39,"value":304},{"type":26,"tag":87,"props":3476,"children":3477},{"style":99},[3478],{"type":39,"value":3479},"host\n",{"type":26,"tag":87,"props":3481,"children":3482},{"class":89,"line":366},[3483,3487],{"type":26,"tag":87,"props":3484,"children":3485},{"style":230},[3486],{"type":39,"value":1829},{"type":26,"tag":87,"props":3488,"children":3489},{"style":236},[3490],{"type":39,"value":239},{"type":26,"tag":87,"props":3492,"children":3493},{"class":89,"line":384},[3494,3498],{"type":26,"tag":87,"props":3495,"children":3496},{"style":236},[3497],{"type":39,"value":873},{"type":26,"tag":87,"props":3499,"children":3500},{"style":99},[3501],{"type":39,"value":3502},"--certificatesresolvers.letsencrypt.acme.httpchallenge=true\n",{"type":26,"tag":87,"props":3504,"children":3505},{"class":89,"line":402},[3506,3510],{"type":26,"tag":87,"props":3507,"children":3508},{"style":236},[3509],{"type":39,"value":873},{"type":26,"tag":87,"props":3511,"children":3512},{"style":99},[3513],{"type":39,"value":3514},"--certificatesresolvers.letsencrypt.acme.email=\u003Cyour_email_here>\n",{"type":26,"tag":87,"props":3516,"children":3517},{"class":89,"line":415},[3518,3522],{"type":26,"tag":87,"props":3519,"children":3520},{"style":236},[3521],{"type":39,"value":873},{"type":26,"tag":87,"props":3523,"children":3524},{"style":99},[3525],{"type":39,"value":3526},"--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\n",{"type":26,"tag":87,"props":3528,"children":3529},{"class":89,"line":215},[3530,3534],{"type":26,"tag":87,"props":3531,"children":3532},{"style":236},[3533],{"type":39,"value":873},{"type":26,"tag":87,"props":3535,"children":3536},{"style":99},[3537],{"type":39,"value":3538},"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\n",{"type":26,"tag":87,"props":3540,"children":3541},{"class":89,"line":216},[3542],{"type":26,"tag":87,"props":3543,"children":3544},{"emptyLinePlaceholder":260},[3545],{"type":39,"value":263},{"type":26,"tag":87,"props":3547,"children":3548},{"class":89,"line":217},[3549,3553],{"type":26,"tag":87,"props":3550,"children":3551},{"style":236},[3552],{"type":39,"value":873},{"type":26,"tag":87,"props":3554,"children":3555},{"style":99},[3556],{"type":39,"value":3557},"--entrypoints.web.address=:80\n",{"type":26,"tag":87,"props":3559,"children":3560},{"class":89,"line":218},[3561,3565],{"type":26,"tag":87,"props":3562,"children":3563},{"style":236},[3564],{"type":39,"value":873},{"type":26,"tag":87,"props":3566,"children":3567},{"style":99},[3568],{"type":39,"value":3569},"--entrypoints.websecure.address=:443\n",{"type":26,"tag":87,"props":3571,"children":3572},{"class":89,"line":219},[3573],{"type":26,"tag":87,"props":3574,"children":3575},{"style":236},[3576],{"type":39,"value":3577},"      \n",{"type":26,"tag":87,"props":3579,"children":3580},{"class":89,"line":503},[3581,3585],{"type":26,"tag":87,"props":3582,"children":3583},{"style":236},[3584],{"type":39,"value":873},{"type":26,"tag":87,"props":3586,"children":3587},{"style":99},[3588],{"type":39,"value":3589},"--entrypoints.web.http.redirections.entrypoint.to=websecure\n",{"type":26,"tag":87,"props":3591,"children":3592},{"class":89,"line":516},[3593,3597],{"type":26,"tag":87,"props":3594,"children":3595},{"style":236},[3596],{"type":39,"value":873},{"type":26,"tag":87,"props":3598,"children":3599},{"style":99},[3600],{"type":39,"value":3601},"--entrypoints.web.http.redirections.entrypoint.scheme=https\n",{"type":26,"tag":87,"props":3603,"children":3604},{"class":89,"line":538},[3605,3609],{"type":26,"tag":87,"props":3606,"children":3607},{"style":236},[3608],{"type":39,"value":873},{"type":26,"tag":87,"props":3610,"children":3611},{"style":99},[3612],{"type":39,"value":3613},"--entrypoints.websecure.http.tls.certresolver=letsencrypt\n",{"type":26,"tag":87,"props":3615,"children":3616},{"class":89,"line":556},[3617],{"type":26,"tag":87,"props":3618,"children":3619},{"emptyLinePlaceholder":260},[3620],{"type":39,"value":263},{"type":26,"tag":87,"props":3622,"children":3623},{"class":89,"line":596},[3624,3628],{"type":26,"tag":87,"props":3625,"children":3626},{"style":236},[3627],{"type":39,"value":873},{"type":26,"tag":87,"props":3629,"children":3630},{"style":99},[3631],{"type":39,"value":3632},"--providers.docker\n",{"type":26,"tag":87,"props":3634,"children":3635},{"class":89,"line":894},[3636,3640],{"type":26,"tag":87,"props":3637,"children":3638},{"style":236},[3639],{"type":39,"value":873},{"type":26,"tag":87,"props":3641,"children":3642},{"style":99},[3643],{"type":39,"value":3644},"--providers.docker.defaultrule=Host(`{{ trimPrefix `app-` .Name }}.example.com`)\n",{"type":26,"tag":87,"props":3646,"children":3647},{"class":89,"line":906},[3648,3652],{"type":26,"tag":87,"props":3649,"children":3650},{"style":236},[3651],{"type":39,"value":873},{"type":26,"tag":87,"props":3653,"children":3654},{"style":99},[3655],{"type":39,"value":3656},"--providers.docker.constraints=Label(`com.docker.compose.service`,`app`)\n",{"type":26,"tag":87,"props":3658,"children":3659},{"class":89,"line":926},[3660,3664],{"type":26,"tag":87,"props":3661,"children":3662},{"style":230},[3663],{"type":39,"value":1745},{"type":26,"tag":87,"props":3665,"children":3666},{"style":236},[3667],{"type":39,"value":239},{"type":26,"tag":87,"props":3669,"children":3670},{"class":89,"line":942},[3671,3675],{"type":26,"tag":87,"props":3672,"children":3673},{"style":236},[3674],{"type":39,"value":873},{"type":26,"tag":87,"props":3676,"children":3677},{"style":99},[3678],{"type":39,"value":3679},"/var/run/docker.sock:/var/run/docker.sock:ro\n",{"type":26,"tag":87,"props":3681,"children":3682},{"class":89,"line":962},[3683,3687],{"type":26,"tag":87,"props":3684,"children":3685},{"style":236},[3686],{"type":39,"value":873},{"type":26,"tag":87,"props":3688,"children":3689},{"style":99},[3690],{"type":39,"value":3691},"/letsencrypt:/letsencrypt\n",{"type":26,"tag":35,"props":3693,"children":3694},{},[3695],{"type":39,"value":3696},"Let's explain this a little",{"type":26,"tag":1174,"props":3698,"children":3699},{},[3700,3713,3718,3723,3728],{"type":26,"tag":128,"props":3701,"children":3702},{},[3703,3705,3711],{"type":39,"value":3704},"We configure a ",{"type":26,"tag":42,"props":3706,"children":3708},{"href":3707},"https://letsencrypt.org/",[3709],{"type":39,"value":3710},"Let's Encrypt",{"type":39,"value":3712}," certificates resolvers (11-14)",{"type":26,"tag":128,"props":3714,"children":3715},{},[3716],{"type":39,"value":3717},"We listen on port 80 and 443 (16-17)",{"type":26,"tag":128,"props":3719,"children":3720},{},[3721],{"type":39,"value":3722},"We redirect all http traffic to https (19-20)",{"type":26,"tag":128,"props":3724,"children":3725},{},[3726],{"type":39,"value":3727},"and attach the configured certificates resolvers to it (21)",{"type":26,"tag":128,"props":3729,"children":3730},{},[3731],{"type":39,"value":3732},"We setup the docker provider (23-25)",{"type":26,"tag":3734,"props":3735,"children":3737},"h3",{"id":3736},"explanation-for-lines-24-25",[3738],{"type":39,"value":3739},"Explanation for lines 24-25",{"type":26,"tag":76,"props":3741,"children":3743},{"code":3742},"--providers.docker.defaultrule=Host(`{{ trimPrefix 'app-'.Name }}.example.com`)\n",[3744],{"type":26,"tag":83,"props":3745,"children":3746},{"__ignoreMap":7},[3747],{"type":39,"value":3742},{"type":26,"tag":124,"props":3749,"children":3750},{},[3751,3783],{"type":26,"tag":128,"props":3752,"children":3753},{},[3754,3760,3762,3768,3770,3775,3777],{"type":26,"tag":83,"props":3755,"children":3757},{"className":3756},[],[3758],{"type":39,"value":3759},".Name",{"type":39,"value":3761}," is autogenerated as ",{"type":26,"tag":83,"props":3763,"children":3765},{"className":3764},[],[3766],{"type":39,"value":3767},"\u003Cservice-name>-\u003Cstack_name>",{"type":39,"value":3769}," so for your ",{"type":26,"tag":83,"props":3771,"children":3773},{"className":3772},[],[3774],{"type":39,"value":2984},{"type":39,"value":3776}," example it would be ",{"type":26,"tag":83,"props":3778,"children":3780},{"className":3779},[],[3781],{"type":39,"value":3782},"app-whoami",{"type":26,"tag":128,"props":3784,"children":3785},{},[3786,3792,3794],{"type":26,"tag":83,"props":3787,"children":3789},{"className":3788},[],[3790],{"type":39,"value":3791},"trimPrefix 'app-'.Name",{"type":39,"value":3793}," resolves in ",{"type":26,"tag":83,"props":3795,"children":3797},{"className":3796},[],[3798],{"type":39,"value":2984},{"type":26,"tag":3800,"props":3801,"children":3802},"br",{},[],{"type":26,"tag":76,"props":3804,"children":3805},{"code":3656},[3806],{"type":26,"tag":83,"props":3807,"children":3808},{"__ignoreMap":7},[3809],{"type":39,"value":3656},{"type":26,"tag":35,"props":3811,"children":3812},{},[3813,3815,3820,3822,3828,3830,3836],{"type":39,"value":3814},"All services should be exposed by default but should be filtered down, only to ",{"type":26,"tag":83,"props":3816,"children":3818},{"className":3817},[],[3819],{"type":39,"value":3283},{"type":39,"value":3821}," services. The label ",{"type":26,"tag":83,"props":3823,"children":3825},{"className":3824},[],[3826],{"type":39,"value":3827},"com.docker.compose.service",{"type":39,"value":3829}," is added by ",{"type":26,"tag":83,"props":3831,"children":3833},{"className":3832},[],[3834],{"type":39,"value":3835},"docker-compose",{"type":39,"value":3837}," to all containers .",{"type":26,"tag":64,"props":3839,"children":3841},{"id":3840},"bonus-configuration",[3842],{"type":39,"value":3843},"Bonus configuration",{"type":26,"tag":35,"props":3845,"children":3846},{},[3847],{"type":39,"value":3848},"We can tweak this configuration even more.",{"type":26,"tag":3734,"props":3850,"children":3852},{"id":3851},"exposing-other-services-in-the-stack",[3853],{"type":39,"value":3854},"Exposing other services in the stack",{"type":26,"tag":35,"props":3856,"children":3857},{},[3858],{"type":39,"value":3859},"Sometimes you want to expose more than just the app container.",{"type":26,"tag":35,"props":3861,"children":3862},{},[3863,3865,3870],{"type":39,"value":3864},"The current configuration won't route traffic to any other services other than ",{"type":26,"tag":83,"props":3866,"children":3868},{"className":3867},[],[3869],{"type":39,"value":3283},{"type":39,"value":3871},".\nTo still be able to use the default configuration method, we need to re-enable it.",{"type":26,"tag":76,"props":3873,"children":3875},{"code":3874},"--providers.docker.constraints=Label(`com.docker.compose.service`,`app`) || Label(`traefik.enable`, `true`)\n",[3876],{"type":26,"tag":83,"props":3877,"children":3878},{"__ignoreMap":7},[3879],{"type":39,"value":3874},{"type":26,"tag":3734,"props":3881,"children":3883},{"id":3882},"stack-name-other-than-directory-name",[3884],{"type":39,"value":3885},"Stack name other than directory name",{"type":26,"tag":35,"props":3887,"children":3888},{},[3889,3891,3896],{"type":39,"value":3890},"When deploying the stacks, the name is generated based on directory name where the ",{"type":26,"tag":83,"props":3892,"children":3894},{"className":3893},[],[3895],{"type":39,"value":3117},{"type":39,"value":3897}," file is located.\nWe can change the make in several ways, but here is the easiest one:",{"type":26,"tag":76,"props":3899,"children":3903},{"code":3900,"filename":3901,"highlights":3902,"language":220,"meta":7,"className":210,"style":7},"name: whoami\nservices:\n  app:\n    image: traefik/whoami\n","whoami-example/docker-compose.yaml",[90],[3904],{"type":26,"tag":83,"props":3905,"children":3906},{"__ignoreMap":7},[3907,3925,3936,3947],{"type":26,"tag":87,"props":3908,"children":3910},{"class":3909,"line":90},[89,293],[3911,3916,3920],{"type":26,"tag":87,"props":3912,"children":3913},{"style":230},[3914],{"type":39,"value":3915},"name",{"type":26,"tag":87,"props":3917,"children":3918},{"style":236},[3919],{"type":39,"value":304},{"type":26,"tag":87,"props":3921,"children":3922},{"style":99},[3923],{"type":39,"value":3924},"whoami\n",{"type":26,"tag":87,"props":3926,"children":3927},{"class":89,"line":242},[3928,3932],{"type":26,"tag":87,"props":3929,"children":3930},{"style":230},[3931],{"type":39,"value":1688},{"type":26,"tag":87,"props":3933,"children":3934},{"style":236},[3935],{"type":39,"value":239},{"type":26,"tag":87,"props":3937,"children":3938},{"class":89,"line":256},[3939,3943],{"type":26,"tag":87,"props":3940,"children":3941},{"style":230},[3942],{"type":39,"value":3323},{"type":26,"tag":87,"props":3944,"children":3945},{"style":236},[3946],{"type":39,"value":239},{"type":26,"tag":87,"props":3948,"children":3949},{"class":89,"line":266},[3950,3954,3958],{"type":26,"tag":87,"props":3951,"children":3952},{"style":230},[3953],{"type":39,"value":1711},{"type":26,"tag":87,"props":3955,"children":3956},{"style":236},[3957],{"type":39,"value":304},{"type":26,"tag":87,"props":3959,"children":3960},{"style":99},[3961],{"type":39,"value":2890},{"type":26,"tag":1193,"props":3963,"children":3964},{},[3965],{"type":39,"value":1197},{"title":7,"searchDepth":242,"depth":242,"links":3967},[3968,3969,3972],{"id":3048,"depth":242,"text":3051},{"id":3140,"depth":242,"text":3143,"children":3970},[3971],{"id":3736,"depth":256,"text":3739},{"id":3840,"depth":242,"text":3843,"children":3973},[3974,3975],{"id":3851,"depth":256,"text":3854},{"id":3882,"depth":256,"text":3885},{"_path":1234,"_dir":1235,"_draft":6,"_partial":6,"_locale":7,"slug":10,"teams":3977,"primaryTeam":1238,"firstName":1239,"lastName":1240,"prefixTitle":7,"suffixTitle":1241,"education":3978,"role":3980,"workingSince":1252,"inTheCompanySince":1253,"techSkills":3981,"skills":3995,"projects":4005,"contactDetails":4010,"_image":1325,"image":1326,"_id":1327,"_type":1328,"title":1329,"_source":1235,"_file":1330,"_stem":1331,"_extension":1328},[1237,1238],[3979],[1244,1245,1246],[1248,1249,1250,1251],[3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,3992,3993,3994],{"name":1256,"level":1257,"icon":1258},{"name":1260,"level":1257},{"name":1262,"level":1257,"icon":1263},{"name":1265,"level":1257,"icon":1266},{"name":1268,"level":1257,"icon":1269},{"name":1271,"level":1257},{"name":1273,"level":1257,"icon":1274},{"name":1276,"level":1257,"icon":1277},{"name":1279,"level":1280,"icon":1281},{"name":1283,"level":1280,"icon":1284},{"name":1286,"level":1280},{"name":1288,"level":1280,"icon":1289},{"name":1291,"level":1280,"icon":1292},[3996,3997,3998,3999,4000,4001,4002,4003,4004],{"name":1295,"level":1257},{"name":1297,"level":1257},{"name":1238,"level":1257},{"name":1300,"level":1257},{"name":1302,"level":1257},{"name":1304,"level":1280},{"name":1306,"level":1280},{"name":1308,"level":1280},{"name":1310,"level":1280},[4006,4008,4009],{"project":1313,"position":4007},[1315,1316],{"project":1318,"position":1316},{"project":1320,"position":1316},{"eMail":1322,"phone":1323,"visibility":1324},[4012,4026,4038,4050],{"_path":4013,"_dir":4014,"_draft":6,"_partial":260,"_locale":7,"name":4015,"slug":4014,"text":4016,"hoverText":4017,"image":4018,"customer":4019,"tags":4020,"_id":4022,"_type":220,"title":4023,"_source":1204,"_file":4024,"_stem":4025,"_extension":220},"/en/portfolio/bitburger/_teaser","bitburger","Bitburger B2B and Brand Shops","Relaunch of Bitburger Brewery Group online shops with new platform","Migration of all brand shops and B2B shop of Bitburger Brewery Group to a unified platform. Ongoing support for redesign initiatives and integration of additional subsystems.","/images/portfolio/bitburger/Bitburger_HuW_Glaeser.jpg","Bitburger",[20,4021],"e-commerce","common:en:portfolio:900.bitburger:_teaser.yaml","Teaser","en/portfolio/900.bitburger/_teaser.yaml","en/portfolio/900.bitburger/_teaser",{"_path":4027,"_dir":4028,"_draft":6,"_partial":260,"_locale":7,"name":4029,"slug":4028,"text":4030,"hoverText":4031,"image":4032,"customer":4033,"tags":4034,"_id":4035,"_type":220,"title":4023,"_source":1204,"_file":4036,"_stem":4037,"_extension":220},"/en/portfolio/purize/_teaser","purize","Purize Filters","E-Commerce for high-quality activated carbon filters","We support PURIZE® Filters with a complete e-commerce service including fulfillment solution and connection to shipping service providers. Our expertise in online retail enables the manufacturer of high-quality activated carbon filters \"Made in Germany\" to process their orders smoothly.","/images/portfolio/barcode-machine-verdandijpg.jpg","PURIZE® Filters",[20,4021],"common:en:portfolio:9000.purize:_teaser.yaml","en/portfolio/9000.purize/_teaser.yaml","en/portfolio/9000.purize/_teaser",{"_path":4039,"_dir":4040,"_draft":6,"_partial":260,"_locale":7,"name":4041,"slug":4040,"text":4042,"hoverText":4043,"image":4044,"customer":4041,"tags":4045,"_id":4047,"_type":220,"title":4023,"_source":1204,"_file":4048,"_stem":4049,"_extension":220},"/en/portfolio/pixelx/_teaser","pixelx","PixelX","IT Security with Precision and Expertise","For PixelX, we conducted a targeted security analysis where, thanks to our deep technical understanding, we were able to identify a critical SQL injection vulnerability. With minimal time investment, we achieved maximum security gain.","/images/portfolio/pixelx/pixelx_secured.png",[4046,21],"security","common:en:portfolio:9010.pixelx:_teaser.yaml","en/portfolio/9010.pixelx/_teaser.yaml","en/portfolio/9010.pixelx/_teaser",{"_path":4051,"_dir":4052,"_draft":6,"_partial":260,"_locale":7,"name":4053,"slug":4052,"text":4054,"hoverText":4055,"image":4056,"customer":4053,"tags":4057,"_id":4058,"_type":220,"title":4023,"_source":1204,"_file":4059,"_stem":4060,"_extension":220},"/en/portfolio/slimspots/_teaser","slimspots","SlimSpots","Processing large amounts of data in real-time","For SlimSpots, a global provider of ad marketing solutions, we developed a highly scalable infrastructure that enables the processing of trillions of data records in real-time.","/images/portfolio/slimspots/slim_spots_prtfolio.png",[1306,21],"common:en:portfolio:9020.slimspots:_teaser.yaml","en/portfolio/9020.slimspots/_teaser.yaml","en/portfolio/9020.slimspots/_teaser",1782284057317]