[{"data":1,"prerenderedAt":1474},["ShallowReactive",2],{"\u002Fblog\u002Flabfromzero-3":3,"\u002Fblog\u002Flabfromzero-3-surround":1463},{"id":4,"title":5,"authors":6,"badge":7,"body":9,"date":1452,"description":1453,"extension":1454,"image":1455,"meta":1457,"navigation":1458,"path":1459,"seo":1460,"stem":1461,"__hash__":1462},"posts\u002F1.blog\u002F5.LabFromZero-3\u002Findex.md","LabFromZero Part 3 - Cluster Bootstraping and GitOps",null,{"label":8},"GitOps, FluxCD, Bootstrapping",{"type":10,"value":11,"toc":1443},"minimark",[12,17,29,33,36,55,60,65,68,71,296,299,485,489,492,503,507,516,520,523,621,624,631,692,695,699,702,722,726,730,733,744,748,751,1034,1037,1383,1387,1390,1420,1423,1431,1439],[13,14,16],"h2",{"id":15},"premise","Premise",[18,19,20,21,28],"p",{},"With the network, PKI, encrypted nodes, Kubernetes cluster and YubiHSM foundation in place, the baseline is ready to build the declarative deployment on top. This post covers the GitOps bootstrap based on the ",[22,23,27],"a",{"href":24,"rel":25},"https:\u002F\u002Fcodeberg.org\u002Ftessellative",[26],"nofollow","LabFromZero repository",".",[13,30,32],{"id":31},"the-chicken-and-egg-problems","The chicken-and-egg problems",[18,34,35],{},"There are many chicken and egg problems at this stage. The does not have:",[37,38,39,43,46,49,52],"ul",{},[40,41,42],"li",{},"Networking so there is no pod communication, DNS, network policies, etc",[40,44,45],{},"Storage capability so no services with persistence",[40,47,48],{},"Cert-manager so no services are exp",[40,50,51],{},"Ingress \u002F Gateway to serve http traffic externally",[40,53,54],{},"FluxCD controller",[56,57,59],"h3",{"id":58},"first-things-first","First Things First",[61,62,64],"h4",{"id":63},"cilium-cni","Cilium CNI",[18,66,67],{},"Install Cilium CNI first, cluster networking is required",[18,69,70],{},"Install it with helm with the following values:",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","kubeProxyReplacement: true\nk8sServiceHost: \"10.10.0.11\"\nk8sServicePort: \"6443\"\ngatewayAPI:\n  enabled: true\n  enableAlpn: true\nexternalIPs:\n  enabled: true\nl2announcements:\n  enabled: true\noperator:\n  replicas: 1\n  nodeSelector:\n    node-role.kubernetes.io\u002Fcontrol-plane: \"\"\nipam:\n  operator:\n    clusterPoolIPv4PodCIDRList:\n      - 10.244.0.0\u002F16\n    clusterPoolIPv4MaskSize: 24\nbpf:\n  datapathMode: veth\n","yaml","",[79,80,81,98,116,131,140,150,160,168,177,185,194,202,214,222,233,241,249,257,266,277,285],"code",{"__ignoreMap":77},[82,83,86,90,94],"span",{"class":84,"line":85},"line",1,[82,87,89],{"class":88},"swJcz","kubeProxyReplacement",[82,91,93],{"class":92},"sMK4o",":",[82,95,97],{"class":96},"sfNiH"," true\n",[82,99,101,104,106,109,113],{"class":84,"line":100},2,[82,102,103],{"class":88},"k8sServiceHost",[82,105,93],{"class":92},[82,107,108],{"class":92}," \"",[82,110,112],{"class":111},"sfazB","10.10.0.11",[82,114,115],{"class":92},"\"\n",[82,117,119,122,124,126,129],{"class":84,"line":118},3,[82,120,121],{"class":88},"k8sServicePort",[82,123,93],{"class":92},[82,125,108],{"class":92},[82,127,128],{"class":111},"6443",[82,130,115],{"class":92},[82,132,134,137],{"class":84,"line":133},4,[82,135,136],{"class":88},"gatewayAPI",[82,138,139],{"class":92},":\n",[82,141,143,146,148],{"class":84,"line":142},5,[82,144,145],{"class":88},"  enabled",[82,147,93],{"class":92},[82,149,97],{"class":96},[82,151,153,156,158],{"class":84,"line":152},6,[82,154,155],{"class":88},"  enableAlpn",[82,157,93],{"class":92},[82,159,97],{"class":96},[82,161,163,166],{"class":84,"line":162},7,[82,164,165],{"class":88},"externalIPs",[82,167,139],{"class":92},[82,169,171,173,175],{"class":84,"line":170},8,[82,172,145],{"class":88},[82,174,93],{"class":92},[82,176,97],{"class":96},[82,178,180,183],{"class":84,"line":179},9,[82,181,182],{"class":88},"l2announcements",[82,184,139],{"class":92},[82,186,188,190,192],{"class":84,"line":187},10,[82,189,145],{"class":88},[82,191,93],{"class":92},[82,193,97],{"class":96},[82,195,197,200],{"class":84,"line":196},11,[82,198,199],{"class":88},"operator",[82,201,139],{"class":92},[82,203,205,208,210],{"class":84,"line":204},12,[82,206,207],{"class":88},"  replicas",[82,209,93],{"class":92},[82,211,213],{"class":212},"sbssI"," 1\n",[82,215,217,220],{"class":84,"line":216},13,[82,218,219],{"class":88},"  nodeSelector",[82,221,139],{"class":92},[82,223,225,228,230],{"class":84,"line":224},14,[82,226,227],{"class":88},"    node-role.kubernetes.io\u002Fcontrol-plane",[82,229,93],{"class":92},[82,231,232],{"class":92}," \"\"\n",[82,234,236,239],{"class":84,"line":235},15,[82,237,238],{"class":88},"ipam",[82,240,139],{"class":92},[82,242,244,247],{"class":84,"line":243},16,[82,245,246],{"class":88},"  operator",[82,248,139],{"class":92},[82,250,252,255],{"class":84,"line":251},17,[82,253,254],{"class":88},"    clusterPoolIPv4PodCIDRList",[82,256,139],{"class":92},[82,258,260,263],{"class":84,"line":259},18,[82,261,262],{"class":92},"      -",[82,264,265],{"class":111}," 10.244.0.0\u002F16\n",[82,267,269,272,274],{"class":84,"line":268},19,[82,270,271],{"class":88},"    clusterPoolIPv4MaskSize",[82,273,93],{"class":92},[82,275,276],{"class":212}," 24\n",[82,278,280,283],{"class":84,"line":279},20,[82,281,282],{"class":88},"bpf",[82,284,139],{"class":92},[82,286,288,291,293],{"class":84,"line":287},21,[82,289,290],{"class":88},"  datapathMode",[82,292,93],{"class":92},[82,294,295],{"class":111}," veth\n",[18,297,298],{},"Initial Gateway configuration",[72,300,302],{"className":74,"code":301,"language":76,"meta":77,"style":77},"apiVersion: gateway.networking.k8s.io\u002Fv1\nkind: Gateway\nmetadata:\n  name: static-external\n  namespace: kube-system\nspec:\n  gatewayClassName: cilium\n  addresses:\n    - type: IPAddress\n      value: 10.10.64.1\n  allowedListeners:\n    namespaces:\n      from: All\n  listeners:\n    - name: http\n      protocol: HTTP\n      port: 80\n      allowedRoutes:\n        namespaces:\n          from: Same\n\n",[79,303,304,314,324,331,341,351,358,368,375,388,398,405,412,422,429,441,451,461,468,475],{"__ignoreMap":77},[82,305,306,309,311],{"class":84,"line":85},[82,307,308],{"class":88},"apiVersion",[82,310,93],{"class":92},[82,312,313],{"class":111}," gateway.networking.k8s.io\u002Fv1\n",[82,315,316,319,321],{"class":84,"line":100},[82,317,318],{"class":88},"kind",[82,320,93],{"class":92},[82,322,323],{"class":111}," Gateway\n",[82,325,326,329],{"class":84,"line":118},[82,327,328],{"class":88},"metadata",[82,330,139],{"class":92},[82,332,333,336,338],{"class":84,"line":133},[82,334,335],{"class":88},"  name",[82,337,93],{"class":92},[82,339,340],{"class":111}," static-external\n",[82,342,343,346,348],{"class":84,"line":142},[82,344,345],{"class":88},"  namespace",[82,347,93],{"class":92},[82,349,350],{"class":111}," kube-system\n",[82,352,353,356],{"class":84,"line":152},[82,354,355],{"class":88},"spec",[82,357,139],{"class":92},[82,359,360,363,365],{"class":84,"line":162},[82,361,362],{"class":88},"  gatewayClassName",[82,364,93],{"class":92},[82,366,367],{"class":111}," cilium\n",[82,369,370,373],{"class":84,"line":170},[82,371,372],{"class":88},"  addresses",[82,374,139],{"class":92},[82,376,377,380,383,385],{"class":84,"line":179},[82,378,379],{"class":92},"    -",[82,381,382],{"class":88}," type",[82,384,93],{"class":92},[82,386,387],{"class":111}," IPAddress\n",[82,389,390,393,395],{"class":84,"line":187},[82,391,392],{"class":88},"      value",[82,394,93],{"class":92},[82,396,397],{"class":212}," 10.10.64.1\n",[82,399,400,403],{"class":84,"line":196},[82,401,402],{"class":88},"  allowedListeners",[82,404,139],{"class":92},[82,406,407,410],{"class":84,"line":204},[82,408,409],{"class":88},"    namespaces",[82,411,139],{"class":92},[82,413,414,417,419],{"class":84,"line":216},[82,415,416],{"class":88},"      from",[82,418,93],{"class":92},[82,420,421],{"class":111}," All\n",[82,423,424,427],{"class":84,"line":224},[82,425,426],{"class":88},"  listeners",[82,428,139],{"class":92},[82,430,431,433,436,438],{"class":84,"line":235},[82,432,379],{"class":92},[82,434,435],{"class":88}," name",[82,437,93],{"class":92},[82,439,440],{"class":111}," http\n",[82,442,443,446,448],{"class":84,"line":243},[82,444,445],{"class":88},"      protocol",[82,447,93],{"class":92},[82,449,450],{"class":111}," HTTP\n",[82,452,453,456,458],{"class":84,"line":251},[82,454,455],{"class":88},"      port",[82,457,93],{"class":92},[82,459,460],{"class":212}," 80\n",[82,462,463,466],{"class":84,"line":259},[82,464,465],{"class":88},"      allowedRoutes",[82,467,139],{"class":92},[82,469,470,473],{"class":84,"line":268},[82,471,472],{"class":88},"        namespaces",[82,474,139],{"class":92},[82,476,477,480,482],{"class":84,"line":279},[82,478,479],{"class":88},"          from",[82,481,93],{"class":92},[82,483,484],{"class":111}," Same\n",[61,486,488],{"id":487},"gateway-api-crds","Gateway API CRDs",[18,490,491],{},"Add the gateway api CRDs",[72,493,497],{"className":494,"code":495,"language":496,"meta":77,"style":77},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","kubectl apply --server-side -f https:\u002F\u002Fgithub.com\u002Fkubernetes-sigs\u002Fgateway-api\u002Freleases\u002Fdownload\u002Fv1.6.1\u002Fstandard-install.yaml\n","sh",[79,498,499],{"__ignoreMap":77},[82,500,501],{"class":84,"line":85},[82,502,495],{},[61,504,506],{"id":505},"local-path-storage","Local Path Storage",[72,508,510],{"className":494,"code":509,"language":496,"meta":77,"style":77},"kubectl apply -f https:\u002F\u002Fraw.githubusercontent.com\u002Francher\u002Flocal-path-provisioner\u002Fv0.0.37\u002Fdeploy\u002Flocal-path-storage.yaml\n",[79,511,512],{"__ignoreMap":77},[82,513,514],{"class":84,"line":85},[82,515,509],{},[61,517,519],{"id":518},"cert-manager","Cert Manager",[18,521,522],{},"Install cert-manager via helm with the following values",[72,524,526],{"className":74,"code":525,"language":76,"meta":77,"style":77},"global:\n  nodeSelector:\n    node-role.kubernetes.io\u002Fcontrol-plane: \"\"\nenabled: true\ncrds:\n  enabled: true\nconfig:\n  gatewayAPI:\n    enabled: true\n    enableListenerSet: true\n  featureGates:\n    ListenerSets: true\n",[79,527,528,535,541,549,558,565,573,580,587,596,605,612],{"__ignoreMap":77},[82,529,530,533],{"class":84,"line":85},[82,531,532],{"class":88},"global",[82,534,139],{"class":92},[82,536,537,539],{"class":84,"line":100},[82,538,219],{"class":88},[82,540,139],{"class":92},[82,542,543,545,547],{"class":84,"line":118},[82,544,227],{"class":88},[82,546,93],{"class":92},[82,548,232],{"class":92},[82,550,551,554,556],{"class":84,"line":133},[82,552,553],{"class":88},"enabled",[82,555,93],{"class":92},[82,557,97],{"class":96},[82,559,560,563],{"class":84,"line":142},[82,561,562],{"class":88},"crds",[82,564,139],{"class":92},[82,566,567,569,571],{"class":84,"line":152},[82,568,145],{"class":88},[82,570,93],{"class":92},[82,572,97],{"class":96},[82,574,575,578],{"class":84,"line":162},[82,576,577],{"class":88},"config",[82,579,139],{"class":92},[82,581,582,585],{"class":84,"line":170},[82,583,584],{"class":88},"  gatewayAPI",[82,586,139],{"class":92},[82,588,589,592,594],{"class":84,"line":179},[82,590,591],{"class":88},"    enabled",[82,593,93],{"class":92},[82,595,97],{"class":96},[82,597,598,601,603],{"class":84,"line":187},[82,599,600],{"class":88},"    enableListenerSet",[82,602,93],{"class":92},[82,604,97],{"class":96},[82,606,607,610],{"class":84,"line":196},[82,608,609],{"class":88},"  featureGates",[82,611,139],{"class":92},[82,613,614,617,619],{"class":84,"line":204},[82,615,616],{"class":88},"    ListenerSets",[82,618,93],{"class":92},[82,620,97],{"class":96},[18,622,623],{},"Cert Manager Intermediate issuer",[18,625,626,28],{},[22,627,630],{"href":628,"rel":629},"https:\u002F\u002Fcodeberg.org\u002Ftessellative\u002FLabFromZero\u002Fsrc\u002Fbranch\u002Fmaster\u002F1.0-pki-ca#kubernetes-setup",[26],"Add the cert-manager intermediate ca crt and key from the PKI",[72,632,634],{"className":74,"code":633,"language":76,"meta":77,"style":77},"apiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: labfromzero-intermediate\nspec:\n  ca:\n    secretName: cert-manager-intermediate-ca-key-pair\n",[79,635,636,645,654,660,669,675,682],{"__ignoreMap":77},[82,637,638,640,642],{"class":84,"line":85},[82,639,308],{"class":88},[82,641,93],{"class":92},[82,643,644],{"class":111}," cert-manager.io\u002Fv1\n",[82,646,647,649,651],{"class":84,"line":100},[82,648,318],{"class":88},[82,650,93],{"class":92},[82,652,653],{"class":111}," ClusterIssuer\n",[82,655,656,658],{"class":84,"line":118},[82,657,328],{"class":88},[82,659,139],{"class":92},[82,661,662,664,666],{"class":84,"line":133},[82,663,335],{"class":88},[82,665,93],{"class":92},[82,667,668],{"class":111}," labfromzero-intermediate\n",[82,670,671,673],{"class":84,"line":142},[82,672,355],{"class":88},[82,674,139],{"class":92},[82,676,677,680],{"class":84,"line":152},[82,678,679],{"class":88},"  ca",[82,681,139],{"class":92},[82,683,684,687,689],{"class":84,"line":162},[82,685,686],{"class":88},"    secretName",[82,688,93],{"class":92},[82,690,691],{"class":111}," cert-manager-intermediate-ca-key-pair\n",[18,693,694],{},"After these the cluster should be able to host and serve services externally!",[61,696,698],{"id":697},"fluxcd-controller","FluxCD Controller",[18,700,701],{},"Install it via Helm",[72,703,705],{"className":494,"code":704,"language":496,"meta":77,"style":77},"helm install flux-operator oci:\u002F\u002Fghcr.io\u002Fcontrolplaneio-fluxcd\u002Fcharts\u002Fflux-operator \\\n  --namespace flux-system \\\n  --create-namespace\n",[79,706,707,712,717],{"__ignoreMap":77},[82,708,709],{"class":84,"line":85},[82,710,711],{},"helm install flux-operator oci:\u002F\u002Fghcr.io\u002Fcontrolplaneio-fluxcd\u002Fcharts\u002Fflux-operator \\\n",[82,713,714],{"class":84,"line":100},[82,715,716],{},"  --namespace flux-system \\\n",[82,718,719],{"class":84,"line":118},[82,720,721],{},"  --create-namespace\n",[56,723,725],{"id":724},"fluxcd-bootstrap","FluxCD Bootstrap",[56,727,729],{"id":728},"prerequisites","Prerequisites",[18,731,732],{},"FluxCD wants a Git repository, but the Git service (ForgeJo) itself needs to be deployed. The practical solution used in LabFromZero is:",[37,734,735,738,741],{},[40,736,737],{},"Install ForgeJo with Helm first",[40,739,740],{},"Create the labfromzero ogranization with the bootstrap repository",[40,742,743],{},"Create and add the Flux deployer SSH key to a dedicated technical user",[61,745,747],{"id":746},"install-forgejo-with-helm","Install ForgeJo with Helm",[18,749,750],{},"ForgeJo ListenerSet for the Gateway",[72,752,754],{"className":74,"code":753,"language":76,"meta":77,"style":77},"apiVersion: gateway.networking.k8s.io\u002Fv1\nkind: ListenerSet\nmetadata:\n  name: forgejo\n  annotations:\n    cert-manager.io\u002Fcluster-issuer: labfromzero-intermediate\nspec:\n  parentRef:\n    name: static-external\n    namespace: kube-system\n    kind: Gateway\n    group: gateway.networking.k8s.io\n  listeners:\n    - name: https\n      hostname: git.labfromzero.internal\n      port: 443\n      protocol: HTTPS\n      tls:\n        mode: Terminate\n        certificateRefs:\n          - name: forgejo-tls\n            kind: Secret\n            group: \"\"\n      allowedRoutes:\n        namespaces:\n          from: Same\n    - name: ssh\n      port: 22\n      protocol: TCP\n      allowedRoutes:\n        namespaces:\n          from: Same\n",[79,755,756,764,773,779,788,795,804,810,817,826,835,844,854,860,871,881,890,899,906,916,923,935,946,956,963,970,979,991,1001,1011,1018,1025],{"__ignoreMap":77},[82,757,758,760,762],{"class":84,"line":85},[82,759,308],{"class":88},[82,761,93],{"class":92},[82,763,313],{"class":111},[82,765,766,768,770],{"class":84,"line":100},[82,767,318],{"class":88},[82,769,93],{"class":92},[82,771,772],{"class":111}," ListenerSet\n",[82,774,775,777],{"class":84,"line":118},[82,776,328],{"class":88},[82,778,139],{"class":92},[82,780,781,783,785],{"class":84,"line":133},[82,782,335],{"class":88},[82,784,93],{"class":92},[82,786,787],{"class":111}," forgejo\n",[82,789,790,793],{"class":84,"line":142},[82,791,792],{"class":88},"  annotations",[82,794,139],{"class":92},[82,796,797,800,802],{"class":84,"line":152},[82,798,799],{"class":88},"    cert-manager.io\u002Fcluster-issuer",[82,801,93],{"class":92},[82,803,668],{"class":111},[82,805,806,808],{"class":84,"line":162},[82,807,355],{"class":88},[82,809,139],{"class":92},[82,811,812,815],{"class":84,"line":170},[82,813,814],{"class":88},"  parentRef",[82,816,139],{"class":92},[82,818,819,822,824],{"class":84,"line":179},[82,820,821],{"class":88},"    name",[82,823,93],{"class":92},[82,825,340],{"class":111},[82,827,828,831,833],{"class":84,"line":187},[82,829,830],{"class":88},"    namespace",[82,832,93],{"class":92},[82,834,350],{"class":111},[82,836,837,840,842],{"class":84,"line":196},[82,838,839],{"class":88},"    kind",[82,841,93],{"class":92},[82,843,323],{"class":111},[82,845,846,849,851],{"class":84,"line":204},[82,847,848],{"class":88},"    group",[82,850,93],{"class":92},[82,852,853],{"class":111}," gateway.networking.k8s.io\n",[82,855,856,858],{"class":84,"line":216},[82,857,426],{"class":88},[82,859,139],{"class":92},[82,861,862,864,866,868],{"class":84,"line":224},[82,863,379],{"class":92},[82,865,435],{"class":88},[82,867,93],{"class":92},[82,869,870],{"class":111}," https\n",[82,872,873,876,878],{"class":84,"line":235},[82,874,875],{"class":88},"      hostname",[82,877,93],{"class":92},[82,879,880],{"class":111}," git.labfromzero.internal\n",[82,882,883,885,887],{"class":84,"line":243},[82,884,455],{"class":88},[82,886,93],{"class":92},[82,888,889],{"class":212}," 443\n",[82,891,892,894,896],{"class":84,"line":251},[82,893,445],{"class":88},[82,895,93],{"class":92},[82,897,898],{"class":111}," HTTPS\n",[82,900,901,904],{"class":84,"line":259},[82,902,903],{"class":88},"      tls",[82,905,139],{"class":92},[82,907,908,911,913],{"class":84,"line":268},[82,909,910],{"class":88},"        mode",[82,912,93],{"class":92},[82,914,915],{"class":111}," Terminate\n",[82,917,918,921],{"class":84,"line":279},[82,919,920],{"class":88},"        certificateRefs",[82,922,139],{"class":92},[82,924,925,928,930,932],{"class":84,"line":287},[82,926,927],{"class":92},"          -",[82,929,435],{"class":88},[82,931,93],{"class":92},[82,933,934],{"class":111}," forgejo-tls\n",[82,936,938,941,943],{"class":84,"line":937},22,[82,939,940],{"class":88},"            kind",[82,942,93],{"class":92},[82,944,945],{"class":111}," Secret\n",[82,947,949,952,954],{"class":84,"line":948},23,[82,950,951],{"class":88},"            group",[82,953,93],{"class":92},[82,955,232],{"class":92},[82,957,959,961],{"class":84,"line":958},24,[82,960,465],{"class":88},[82,962,139],{"class":92},[82,964,966,968],{"class":84,"line":965},25,[82,967,472],{"class":88},[82,969,139],{"class":92},[82,971,973,975,977],{"class":84,"line":972},26,[82,974,479],{"class":88},[82,976,93],{"class":92},[82,978,484],{"class":111},[82,980,982,984,986,988],{"class":84,"line":981},27,[82,983,379],{"class":92},[82,985,435],{"class":88},[82,987,93],{"class":92},[82,989,990],{"class":111}," ssh\n",[82,992,994,996,998],{"class":84,"line":993},28,[82,995,455],{"class":88},[82,997,93],{"class":92},[82,999,1000],{"class":212}," 22\n",[82,1002,1004,1006,1008],{"class":84,"line":1003},29,[82,1005,445],{"class":88},[82,1007,93],{"class":92},[82,1009,1010],{"class":111}," TCP\n",[82,1012,1014,1016],{"class":84,"line":1013},30,[82,1015,465],{"class":88},[82,1017,139],{"class":92},[82,1019,1021,1023],{"class":84,"line":1020},31,[82,1022,472],{"class":88},[82,1024,139],{"class":92},[82,1026,1028,1030,1032],{"class":84,"line":1027},32,[82,1029,479],{"class":88},[82,1031,93],{"class":92},[82,1033,484],{"class":111},[18,1035,1036],{},"ForgeJo Helm values:",[72,1038,1040],{"className":74,"code":1039,"language":76,"meta":77,"style":77},"nodeSelector:\n  node-role.kubernetes.io\u002Fcontrol-plane: \"\"\npersistence:\n  enabled: true\n  storageClass: local-path\nhttpRoute:\n  enabled: true\n  hostnames:\n    - git.labfromzero.internal\n  parentRefs:\n    - name: forgejo\n      sectionName: https\n      kind: ListenerSet\n      group: gateway.networking.k8s.io\n  rules:\n    - matches:\n        - path:\n            type: PathPrefix\n            value: \u002F\n      backendRefs:\n        - name: forgejo-http\n          port: 3000\nextraVolumeMounts:\n  - name: host-ca\n    mountPath: \u002Fetc\u002Fssl\u002Fcerts\u002Fca-certificates.crt\n    readOnly: true\nextraVolumes:\n  - name: host-ca\n    hostPath:\n      path: \u002Fetc\u002Fssl\u002Fcerts\u002Fca-certificates.crt\n      type: File\ngitea:\n  config:\n    webhook:\n      ALLOWED_HOST_LIST: flux-webhook.labfromzero.internal\n    service:\n      DISABLE_REGISTRATION: true\n      ALLOW_ONLY_EXTERNAL_REGISTRATION: true\n      SHOW_REGISTRATION_BUTTON: false\n",[79,1041,1042,1049,1058,1065,1073,1083,1090,1098,1105,1111,1118,1128,1137,1146,1155,1162,1171,1181,1191,1201,1208,1219,1229,1236,1248,1258,1267,1274,1284,1291,1300,1310,1317,1325,1333,1344,1352,1362,1372],{"__ignoreMap":77},[82,1043,1044,1047],{"class":84,"line":85},[82,1045,1046],{"class":88},"nodeSelector",[82,1048,139],{"class":92},[82,1050,1051,1054,1056],{"class":84,"line":100},[82,1052,1053],{"class":88},"  node-role.kubernetes.io\u002Fcontrol-plane",[82,1055,93],{"class":92},[82,1057,232],{"class":92},[82,1059,1060,1063],{"class":84,"line":118},[82,1061,1062],{"class":88},"persistence",[82,1064,139],{"class":92},[82,1066,1067,1069,1071],{"class":84,"line":133},[82,1068,145],{"class":88},[82,1070,93],{"class":92},[82,1072,97],{"class":96},[82,1074,1075,1078,1080],{"class":84,"line":142},[82,1076,1077],{"class":88},"  storageClass",[82,1079,93],{"class":92},[82,1081,1082],{"class":111}," local-path\n",[82,1084,1085,1088],{"class":84,"line":152},[82,1086,1087],{"class":88},"httpRoute",[82,1089,139],{"class":92},[82,1091,1092,1094,1096],{"class":84,"line":162},[82,1093,145],{"class":88},[82,1095,93],{"class":92},[82,1097,97],{"class":96},[82,1099,1100,1103],{"class":84,"line":170},[82,1101,1102],{"class":88},"  hostnames",[82,1104,139],{"class":92},[82,1106,1107,1109],{"class":84,"line":179},[82,1108,379],{"class":92},[82,1110,880],{"class":111},[82,1112,1113,1116],{"class":84,"line":187},[82,1114,1115],{"class":88},"  parentRefs",[82,1117,139],{"class":92},[82,1119,1120,1122,1124,1126],{"class":84,"line":196},[82,1121,379],{"class":92},[82,1123,435],{"class":88},[82,1125,93],{"class":92},[82,1127,787],{"class":111},[82,1129,1130,1133,1135],{"class":84,"line":204},[82,1131,1132],{"class":88},"      sectionName",[82,1134,93],{"class":92},[82,1136,870],{"class":111},[82,1138,1139,1142,1144],{"class":84,"line":216},[82,1140,1141],{"class":88},"      kind",[82,1143,93],{"class":92},[82,1145,772],{"class":111},[82,1147,1148,1151,1153],{"class":84,"line":224},[82,1149,1150],{"class":88},"      group",[82,1152,93],{"class":92},[82,1154,853],{"class":111},[82,1156,1157,1160],{"class":84,"line":235},[82,1158,1159],{"class":88},"  rules",[82,1161,139],{"class":92},[82,1163,1164,1166,1169],{"class":84,"line":243},[82,1165,379],{"class":92},[82,1167,1168],{"class":88}," matches",[82,1170,139],{"class":92},[82,1172,1173,1176,1179],{"class":84,"line":251},[82,1174,1175],{"class":92},"        -",[82,1177,1178],{"class":88}," path",[82,1180,139],{"class":92},[82,1182,1183,1186,1188],{"class":84,"line":259},[82,1184,1185],{"class":88},"            type",[82,1187,93],{"class":92},[82,1189,1190],{"class":111}," PathPrefix\n",[82,1192,1193,1196,1198],{"class":84,"line":268},[82,1194,1195],{"class":88},"            value",[82,1197,93],{"class":92},[82,1199,1200],{"class":111}," \u002F\n",[82,1202,1203,1206],{"class":84,"line":279},[82,1204,1205],{"class":88},"      backendRefs",[82,1207,139],{"class":92},[82,1209,1210,1212,1214,1216],{"class":84,"line":287},[82,1211,1175],{"class":92},[82,1213,435],{"class":88},[82,1215,93],{"class":92},[82,1217,1218],{"class":111}," forgejo-http\n",[82,1220,1221,1224,1226],{"class":84,"line":937},[82,1222,1223],{"class":88},"          port",[82,1225,93],{"class":92},[82,1227,1228],{"class":212}," 3000\n",[82,1230,1231,1234],{"class":84,"line":948},[82,1232,1233],{"class":88},"extraVolumeMounts",[82,1235,139],{"class":92},[82,1237,1238,1241,1243,1245],{"class":84,"line":958},[82,1239,1240],{"class":92},"  -",[82,1242,435],{"class":88},[82,1244,93],{"class":92},[82,1246,1247],{"class":111}," host-ca\n",[82,1249,1250,1253,1255],{"class":84,"line":965},[82,1251,1252],{"class":88},"    mountPath",[82,1254,93],{"class":92},[82,1256,1257],{"class":111}," \u002Fetc\u002Fssl\u002Fcerts\u002Fca-certificates.crt\n",[82,1259,1260,1263,1265],{"class":84,"line":972},[82,1261,1262],{"class":88},"    readOnly",[82,1264,93],{"class":92},[82,1266,97],{"class":96},[82,1268,1269,1272],{"class":84,"line":981},[82,1270,1271],{"class":88},"extraVolumes",[82,1273,139],{"class":92},[82,1275,1276,1278,1280,1282],{"class":84,"line":993},[82,1277,1240],{"class":92},[82,1279,435],{"class":88},[82,1281,93],{"class":92},[82,1283,1247],{"class":111},[82,1285,1286,1289],{"class":84,"line":1003},[82,1287,1288],{"class":88},"    hostPath",[82,1290,139],{"class":92},[82,1292,1293,1296,1298],{"class":84,"line":1013},[82,1294,1295],{"class":88},"      path",[82,1297,93],{"class":92},[82,1299,1257],{"class":111},[82,1301,1302,1305,1307],{"class":84,"line":1020},[82,1303,1304],{"class":88},"      type",[82,1306,93],{"class":92},[82,1308,1309],{"class":111}," File\n",[82,1311,1312,1315],{"class":84,"line":1027},[82,1313,1314],{"class":88},"gitea",[82,1316,139],{"class":92},[82,1318,1320,1323],{"class":84,"line":1319},33,[82,1321,1322],{"class":88},"  config",[82,1324,139],{"class":92},[82,1326,1328,1331],{"class":84,"line":1327},34,[82,1329,1330],{"class":88},"    webhook",[82,1332,139],{"class":92},[82,1334,1336,1339,1341],{"class":84,"line":1335},35,[82,1337,1338],{"class":88},"      ALLOWED_HOST_LIST",[82,1340,93],{"class":92},[82,1342,1343],{"class":111}," flux-webhook.labfromzero.internal\n",[82,1345,1347,1350],{"class":84,"line":1346},36,[82,1348,1349],{"class":88},"    service",[82,1351,139],{"class":92},[82,1353,1355,1358,1360],{"class":84,"line":1354},37,[82,1356,1357],{"class":88},"      DISABLE_REGISTRATION",[82,1359,93],{"class":92},[82,1361,97],{"class":96},[82,1363,1365,1368,1370],{"class":84,"line":1364},38,[82,1366,1367],{"class":88},"      ALLOW_ONLY_EXTERNAL_REGISTRATION",[82,1369,93],{"class":92},[82,1371,97],{"class":96},[82,1373,1375,1378,1380],{"class":84,"line":1374},39,[82,1376,1377],{"class":88},"      SHOW_REGISTRATION_BUTTON",[82,1379,93],{"class":92},[82,1381,1382],{"class":96}," false\n",[56,1384,1386],{"id":1385},"flux-bootstrap","Flux Bootstrap",[18,1388,1389],{},"Bootstrap the FluxCD GitOps Repo. Kube",[72,1391,1393],{"className":494,"code":1392,"language":496,"meta":77,"style":77},"flux bootstrap git \\\n  --url=ssh:\u002F\u002Fgit@git.labfromzero.internal\u002FLabFromZero\u002FLabFromZero-gitops.git \\\n  --branch=main \\\n  --path=cluster \\\n  --private-key-file=\u003Cflux-deploy-private-key>\n",[79,1394,1395,1400,1405,1410,1415],{"__ignoreMap":77},[82,1396,1397],{"class":84,"line":85},[82,1398,1399],{},"flux bootstrap git \\\n",[82,1401,1402],{"class":84,"line":100},[82,1403,1404],{},"  --url=ssh:\u002F\u002Fgit@git.labfromzero.internal\u002FLabFromZero\u002FLabFromZero-gitops.git \\\n",[82,1406,1407],{"class":84,"line":118},[82,1408,1409],{},"  --branch=main \\\n",[82,1411,1412],{"class":84,"line":133},[82,1413,1414],{},"  --path=cluster \\\n",[82,1416,1417],{"class":84,"line":142},[82,1418,1419],{},"  --private-key-file=\u003Cflux-deploy-private-key>\n",[18,1421,1422],{},"After this point the cluster should be self-managable. Port the above manually installed services into the GitOps repo.",[18,1424,1425,1430],{},[22,1426,1429],{"href":1427,"rel":1428},"https:\u002F\u002Fcodeberg.org\u002Ftessellative\u002FLabFromZero\u002Fsrc\u002Fbranch\u002Fmaster\u002F3.0-gitops",[26],"The GitOps repository layout"," typically looks like:",[72,1432,1437],{"className":1433,"code":1435,"language":1436},[1434],"language-text","apps\u002F \n  |- forgejo\n  |- headlamp\n  |- homepage\n  |- kube-prometheus-stack\n  |- metrics-server\n  |- openbap\n  |- zitadel\ncluster\u002F # FluxCD Bootstrap Root\n  |- cert-manager\n  |- cilim\n  |- flux-system\n  |- k8s-oidc\n  |- \u003Cservice_name>.yaml # Various customization files for the apps\n","text",[79,1438,1435],{"__ignoreMap":77},[1440,1441,1442],"style",{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}",{"title":77,"searchDepth":100,"depth":100,"links":1444},[1445,1446],{"id":15,"depth":100,"text":16},{"id":31,"depth":100,"text":32,"children":1447},[1448,1449,1450,1451],{"id":58,"depth":118,"text":59},{"id":724,"depth":118,"text":725},{"id":728,"depth":118,"text":729},{"id":1385,"depth":118,"text":1386},"2026-10-11","Platform GitOps bootstrapping for the lab environment","md",{"src":1456},"\u002Fblog\u002FLabFromZero\u002Fcover_p3.webp",{},true,"\u002Fblog\u002Flabfromzero-3",{"title":5,"description":1453},"1.blog\u002F5.LabFromZero-3\u002Findex","DSVdQSb7w_MMk7IQTkd_-uqeOPESjkjQN4LQrsR1RcU",[1464,1469],{"title":1465,"path":1466,"stem":1467,"description":1468,"children":-1},"LabFromZero Part 2 - Network, k8s, HSM","\u002Fblog\u002Flabfromzero-2","1.blog\u002F4.LabFromZero-2\u002Findex","From network to cluster with HSM",{"title":1470,"path":1471,"stem":1472,"description":1473,"children":-1},"LabFromZero Part 4 - IDP and Platform Services","\u002Fblog\u002Flabfromzero-4","1.blog\u002F6.LabFromZero-4\u002Findex","IDP and platform services setup",1791905103952]