[{"data":1,"prerenderedAt":1975},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fdeploy\u002Fupgrading":3,"docs-nav":1749},{"id":4,"title":5,"body":6,"description":1740,"extension":1741,"meta":1742,"navigation":1744,"path":1745,"seo":1746,"stem":1747,"__hash__":1748},"docs\u002Fdocs\u002Fdeploy\u002Fupgrading.md","Upgrading Tabularia on Kubernetes and minikube",{"type":7,"value":8,"toc":1723},"minimark",[9,13,28,31,57,62,65,179,186,192,197,271,274,278,341,345,369,374,384,443,446,449,494,497,508,531,537,555,559,569,576,594,608,612,649,655,659,711,733,737,744,798,808,917,931,937,1018,1029,1035,1039,1049,1258,1265,1269,1282,1323,1329,1353,1358,1409,1426,1451,1661,1664,1668,1671,1716,1719],[10,11,5],"h1",{"id":12},"upgrading-tabularia-on-kubernetes-and-minikube",[14,15,16,17,22,23,27],"p",{},"How to move an installation made with the Helm chart to a newer release: what an upgrade\nchanges and what it keeps, how the database schema is migrated, how the new pods replace\nthe old ones, and what to do when something goes wrong. The first part applies to every\ninstallation; then come the steps ",[18,19,21],"a",{"href":20},"#on-minikube","on minikube"," and\n",[18,24,26],{"href":25},"#on-a-kubernetes-cluster","on a Kubernetes cluster",".",[14,29,30],{},"In short:",[32,33,34,38,41,54],"ol",{},[35,36,37],"li",{},"read the upgrade notes of every release between yours and the new one;",[35,39,40],{},"back up PostgreSQL;",[35,42,43,44,48,49,53],{},"run ",[45,46,47],"code",{},"helm upgrade"," with ",[50,51,52],"strong",{},"the same values file"," you installed with;",[35,55,56],{},"check that every pod is ready and the database is at the new revision.",[58,59,61],"h2",{"id":60},"what-an-upgrade-changes-and-what-it-keeps","What an upgrade changes, and what it keeps",[14,63,64],{},"The chart runs no database and keeps nothing on a volume: its pods can be replaced at\nany moment. What Tabularia knows lives outside them.",[66,67,68,84],"table",{},[69,70,71],"thead",{},[72,73,74,78,81],"tr",{},[75,76,77],"th",{},"What",[75,79,80],{},"Where",[75,82,83],{},"During an upgrade",[85,86,87,102,113,127,138,148,162],"tbody",{},[72,88,89,93,96],{},[90,91,92],"td",{},"Users, folders, permissions, flows and their versions, connections, schedules, runs, the audit log",[90,94,95],{},"PostgreSQL",[90,97,98,101],{},[50,99,100],{},"kept","; its schema is migrated forward. Back it up first",[72,103,104,107,110],{},[90,105,106],{},"The data: uploaded files, datasource snapshots, flow outputs",[90,108,109],{},"the object storage bucket",[90,111,112],{},"kept; an upgrade does not rewrite it",[72,114,115,118,125],{},[90,116,117],{},"The automatic backups of PostgreSQL",[90,119,120,121,124],{},"the bucket, under ",[45,122,123],{},"backups\u002Fpostgres\u002F"," (or a bucket of their own)",[90,126,100],{},[72,128,129,132,135],{},[90,130,131],{},"The task queue and the step cache's index",[90,133,134],{},"Valkey",[90,136,137],{},"kept if Valkey keeps running; losing it only drops queued tasks and starts the cache cold",[72,139,140,143,146],{},[90,141,142],{},"Scripting environments' images",[90,144,145],{},"your container registry",[90,147,100],{},[72,149,150,153,156],{},[90,151,152],{},"The secrets: Fernet key, JWT secret, passwords",[90,154,155],{},"your values file (or your own Secret)",[90,157,158,159],{},"whatever the values say: ",[50,160,161],{},"pass the same ones",[72,163,164,167,176],{},[90,165,166],{},"Temporary files of a pod",[90,168,169,172,173],{},[45,170,171],{},"\u002Ftmp",", an ",[45,174,175],{},"emptyDir",[90,177,178],{},"gone with the pod, by design",[14,180,181,182,185],{},"The ",[50,183,184],{},"Fernet key"," encrypts the passwords saved in connections: if an upgrade runs with a\ndifferent one, those passwords can no longer be read and must be typed again. The JWT\nsecret signs the sessions: a new one signs everyone out.",[58,187,189,190],{"id":188},"what-happens-during-helm-upgrade","What happens during ",[45,191,47],{},[14,193,194,196],{},[45,195,47],{}," changes the Deployments; Kubernetes then replaces their pods, all\nDeployments at the same time.",[198,199,200,214,228,239,253,259,265],"ul",{},[35,201,202,205,206,209,210,213],{},[50,203,204],{},"The gateway and the orchestrator"," roll one pod at a time, a new pod ready before an\nold one stops (",[45,207,208],{},"maxSurge: 1",", ",[45,211,212],{},"maxUnavailable: 0","): the API keeps answering.",[35,215,216,219,220,223,224,227],{},[50,217,218],{},"Each new gateway and orchestrator pod migrates the database first",", in an init\ncontainer called ",[45,221,222],{},"migrate"," that runs ",[45,225,226],{},"python -m app.db.migra"," before the application\nstarts. The first one does the work, in one transaction (all or nothing); the others\nwait on a PostgreSQL lock and find it done. The application itself never migrates: it\nchecks that the database is not behind its code and refuses to start if it is.",[35,229,230,233,234,238],{},[50,231,232],{},"The old pods keep working on the migrated database."," Every revision is written so\nthat the previous release still runs on it (columns are added, never renamed or\ndropped in the same release; see ",[18,235,237],{"href":236},"\u002Fdocs\u002Fdatabase-migrations","database migrations",").\nThat is what lets the old and the new pods serve side by side for a minute.",[35,240,241,244,245,248,249,252],{},[50,242,243],{},"The workers"," stop taking tasks and finish the one in progress before stopping, up to\n",[45,246,247],{},"worker.terminationGracePeriodSeconds"," (one hour) and\n",[45,250,251],{},"previewWorker.terminationGracePeriodSeconds"," (two minutes). A run longer than that is\ncut and marked failed.",[35,254,255,258],{},[50,256,257],{},"Beat"," is a single pod: the old one stops before the new one starts, a pause of a few\nseconds in the periodic housekeeping.",[35,260,261,264],{},[50,262,263],{},"Flow executions running on an old orchestrator pod are interrupted"," when that pod\nstops: they are closed as failed, with a message that says so, and are not resumed.\nThe Jobs of Script and Container nodes they had started are stopped. Scheduled flows\nare not lost: the new orchestrator takes the next slots.",[35,266,267,270],{},[50,268,269],{},"The interface"," rolls like the gateway. A browser tab left open keeps the old version\nuntil it is reloaded.",[14,272,273],{},"A change to the values alone (a setting, a password) rolls the pods the same way: the\npods carry a checksum of the chart's ConfigMap and Secret.",[58,275,277],{"id":276},"before-you-start","Before you start",[198,279,280,299,314,327],{},[35,281,282,285,286,292,293,298],{},[50,283,284],{},"Read the upgrade notes"," of every release between yours and the new one: the\n\"Upgrade note\" entries in ",[18,287,291],{"href":288,"rel":289},"https:\u002F\u002Fgithub.com\u002FJumpAsAService\u002FTabularia\u002Fblob\u002Fmain\u002FCHANGELOG.md",[290],"nofollow","CHANGELOG.md"," and the\n",[18,294,297],{"href":295,"rel":296},"https:\u002F\u002Fgithub.com\u002FJumpAsAService\u002FTabularia\u002Fblob\u002Fmain\u002Finfrastructure\u002Fhelm\u002Ftabularia\u002FREADME.md#upgrades",[290],"chart README → Upgrades",".\nA few releases ask for a step of their own (a value to set first, two upgrades in a\nrow). Skipping releases is fine for the database (the migration applies every\nrevision in order), but the notes of each one still apply.",[35,300,301,304,305,308,309,313],{},[50,302,303],{},"Back up PostgreSQL."," The simplest way: ",[50,306,307],{},"Administration → Backups → Back up now",",\nand wait for it to say ",[310,311,312],"em",{},"Last backup … ago",". Tabularia makes one every night anyway\n(03:00 by default), into the object storage; the steps below make one by hand too. A\nrevision goes forward only: going back to an older release after a broken upgrade\nmeans restoring a backup.",[35,315,316,319,320,322,323,326],{},[50,317,318],{},"Use the values file you installed with."," Never run ",[45,321,47],{}," without\n",[45,324,325],{},"--values",": the chart's defaults would replace your settings and empty the secrets.",[35,328,329,332,333,336,337,340],{},[50,330,331],{},"Pick a quiet moment",": no long flow running. ",[50,334,335],{},"Runs"," shows what is running;\n",[50,338,339],{},"Administration → Cluster"," shows the pods, and the scripts and containers at work.",[58,342,344],{"id":343},"on-minikube","On minikube",[14,346,347,348,352,353,356,357,360,361,364,365,368],{},"The guide ",[18,349,351],{"href":350},"\u002Fdocs\u002Fdeploy\u002Fminikube","Try Tabularia on your computer with minikube"," puts PostgreSQL and\nValkey in the namespace ",[45,354,355],{},"tabularia-deps",", and PostgreSQL's data on a volume (a\nPersistentVolumeClaim called ",[45,358,359],{},"postgres",") on minikube's disk. That volume survives\n",[45,362,363],{},"minikube stop"," and ",[45,366,367],{},"minikube start",", and every upgrade.",[370,371,373],"h3",{"id":372},"1-back-up-the-database","1. Back up the database",[14,375,376,377,379,380,383],{},"In Tabularia, ",[50,378,307],{},": a few seconds later the backup\nis in your bucket, and ",[50,381,382],{},"Download"," saves a copy on your computer. Or by hand, without\nTabularia: the backup is written inside the PostgreSQL pod, then copied to your computer.",[385,386,391],"pre",{"className":387,"code":388,"language":389,"meta":390,"style":390},"language-bash shiki shiki-themes github-dark-dimmed github-light","kubectl exec --namespace tabularia-deps deploy\u002Fpostgres -- pg_dump --username tabularia --format custom --file \u002Ftmp\u002Ftabularia.dump tabularia\n","bash","",[45,392,393],{"__ignoreMap":390},[394,395,398,402,406,410,413,416,419,422,425,428,431,434,437,440],"span",{"class":396,"line":397},"line",1,[394,399,401],{"class":400},"sqRhv","kubectl",[394,403,405],{"class":404},"sXfbr"," exec",[394,407,409],{"class":408},"swcJU"," --namespace",[394,411,412],{"class":404}," tabularia-deps",[394,414,415],{"class":404}," deploy\u002Fpostgres",[394,417,418],{"class":408}," --",[394,420,421],{"class":404}," pg_dump",[394,423,424],{"class":408}," --username",[394,426,427],{"class":404}," tabularia",[394,429,430],{"class":408}," --format",[394,432,433],{"class":404}," custom",[394,435,436],{"class":408}," --file",[394,438,439],{"class":404}," \u002Ftmp\u002Ftabularia.dump",[394,441,442],{"class":404}," tabularia\n",[14,444,445],{},"Then the name of the pod, and the copy (the second command is the same in every shell).",[14,447,448],{},"macOS and Linux:",[385,450,452],{"className":387,"code":451,"language":389,"meta":390,"style":390},"POD=$(kubectl get pod --namespace tabularia-deps -l app=postgres -o jsonpath='{.items[0].metadata.name}')\n",[45,453,454],{"__ignoreMap":390},[394,455,456,460,464,467,469,472,475,477,479,482,485,488,491],{"class":396,"line":397},[394,457,459],{"class":458},"ssh_m","POD",[394,461,463],{"class":462},"s6PUj","=",[394,465,466],{"class":458},"$(",[394,468,401],{"class":400},[394,470,471],{"class":404}," get",[394,473,474],{"class":404}," pod",[394,476,409],{"class":408},[394,478,412],{"class":404},[394,480,481],{"class":408}," -l",[394,483,484],{"class":404}," app=postgres",[394,486,487],{"class":408}," -o",[394,489,490],{"class":404}," jsonpath='{.items[0].metadata.name}'",[394,492,493],{"class":458},")\n",[14,495,496],{},"Windows (PowerShell):",[385,498,502],{"className":499,"code":500,"language":501,"meta":390,"style":390},"language-powershell shiki shiki-themes github-dark-dimmed github-light","$POD = kubectl get pod --namespace tabularia-deps -l app=postgres -o jsonpath='{.items[0].metadata.name}'\n","powershell",[45,503,504],{"__ignoreMap":390},[394,505,506],{"class":396,"line":397},[394,507,500],{},[385,509,511],{"className":387,"code":510,"language":389,"meta":390,"style":390},"kubectl cp \"tabularia-deps\u002F${POD}:\u002Ftmp\u002Ftabularia.dump\" tabularia-backup.dump\n",[45,512,513],{"__ignoreMap":390},[394,514,515,517,520,523,525,528],{"class":396,"line":397},[394,516,401],{"class":400},[394,518,519],{"class":404}," cp",[394,521,522],{"class":404}," \"tabularia-deps\u002F${",[394,524,459],{"class":458},[394,526,527],{"class":404},"}:\u002Ftmp\u002Ftabularia.dump\"",[394,529,530],{"class":404}," tabularia-backup.dump\n",[14,532,533,536],{},[45,534,535],{},"tabularia-backup.dump"," is now in the folder your terminal is in. Keep it until you have\nused the new release for a while.",[538,539,540],"blockquote",{},[14,541,542,543,546,547,550,551,554],{},"Copy the file out with ",[45,544,545],{},"kubectl cp",", not with ",[45,548,549],{},"kubectl exec … > file",": on Windows\nPowerShell the ",[45,552,553],{},">"," rewrites binary output, and the backup would not restore.",[370,556,558],{"id":557},"2-get-the-new-release","2. Get the new release",[14,560,561,562,565,566,568],{},"Download and unzip the new release as in step 3 of the minikube guide, copy your\n",[45,563,564],{},"my-values.yaml"," into its folder, and open the terminal there. The images follow the\nrelease: ",[45,567,564],{}," does not name a version.",[14,570,571,572,575],{},"If the upgrade notes say that ",[45,573,574],{},"infrastructure\u002Fminikube\u002Fdependencies.yaml"," changed,\napply it again. This is safe: the volume and its data stay.",[385,577,579],{"className":387,"code":578,"language":389,"meta":390,"style":390},"kubectl apply -f infrastructure\u002Fminikube\u002Fdependencies.yaml\n",[45,580,581],{"__ignoreMap":390},[394,582,583,585,588,591],{"class":396,"line":397},[394,584,401],{"class":400},[394,586,587],{"class":404}," apply",[394,589,590],{"class":408}," -f",[394,592,593],{"class":404}," infrastructure\u002Fminikube\u002Fdependencies.yaml\n",[538,595,596],{},[14,597,598,603,604,607],{},[50,599,600,601],{},"Never delete the namespace ",[45,602,355],{}," (nor run ",[45,605,606],{},"minikube delete",") to \"start\nclean\": the volume goes with it, and Tabularia's records with the volume.",[370,609,611],{"id":610},"3-upgrade","3. Upgrade",[385,613,615],{"className":387,"code":614,"language":389,"meta":390,"style":390},"helm upgrade tabularia infrastructure\u002Fhelm\u002Ftabularia --namespace tabularia --values my-values.yaml --wait --timeout 20m\n",[45,616,617],{"__ignoreMap":390},[394,618,619,622,625,627,630,632,634,637,640,643,646],{"class":396,"line":397},[394,620,621],{"class":400},"helm",[394,623,624],{"class":404}," upgrade",[394,626,427],{"class":404},[394,628,629],{"class":404}," infrastructure\u002Fhelm\u002Ftabularia",[394,631,409],{"class":408},[394,633,427],{"class":404},[394,635,636],{"class":408}," --values",[394,638,639],{"class":404}," my-values.yaml",[394,641,642],{"class":408}," --wait",[394,644,645],{"class":408}," --timeout",[394,647,648],{"class":404}," 20m\n",[14,650,651,654],{},[45,652,653],{},"--wait"," returns when every new pod is ready, or fails after twenty minutes; the first\ntime a release runs, minikube downloads its images, which takes a few minutes.",[370,656,658],{"id":657},"4-check","4. Check",[385,660,662],{"className":387,"code":661,"language":389,"meta":390,"style":390},"kubectl get pods --namespace tabularia\nkubectl exec --namespace tabularia deploy\u002Ftabularia-tabularia-gateway -c gateway -- python -m app.db.migra stato\n",[45,663,664,677],{"__ignoreMap":390},[394,665,666,668,670,673,675],{"class":396,"line":397},[394,667,401],{"class":400},[394,669,471],{"class":404},[394,671,672],{"class":404}," pods",[394,674,409],{"class":408},[394,676,442],{"class":404},[394,678,680,682,684,686,688,691,694,697,699,702,705,708],{"class":396,"line":679},2,[394,681,401],{"class":400},[394,683,405],{"class":404},[394,685,409],{"class":408},[394,687,427],{"class":404},[394,689,690],{"class":404}," deploy\u002Ftabularia-tabularia-gateway",[394,692,693],{"class":408}," -c",[394,695,696],{"class":404}," gateway",[394,698,418],{"class":408},[394,700,701],{"class":404}," python",[394,703,704],{"class":408}," -m",[394,706,707],{"class":404}," app.db.migra",[394,709,710],{"class":404}," stato\n",[14,712,713,714,364,717,720,721,724,725,728,729,732],{},"Every pod is ",[45,715,716],{},"Running",[45,718,719],{},"1\u002F1",", and the second command prints the same revision for the\ndatabase and the code, for example ",[45,722,723],{},"database: 0020 · code: 0020",". Reload the page in the\nbrowser with ",[50,726,727],{},"Ctrl+Shift+R"," (macOS: ",[50,730,731],{},"Cmd+Shift+R",").",[58,734,736],{"id":735},"on-a-kubernetes-cluster","On a Kubernetes cluster",[14,738,739,740,743],{},"PostgreSQL is usually a managed service, or runs in the cluster under your team's care:\nback it up with the tools that come with it (the provider's snapshot or point-in-time\nrestore, or ",[45,741,742],{},"pg_dump --format custom","). Then:",[385,745,747],{"className":387,"code":746,"language":389,"meta":390,"style":390},"helm upgrade \u003Crelease> infrastructure\u002Fhelm\u002Ftabularia --namespace \u003Cnamespace> \\\n  --values values-production.yaml --wait --timeout 30m\n",[45,748,749,783],{"__ignoreMap":390},[394,750,751,753,755,758,761,764,766,768,770,772,775,777,779],{"class":396,"line":397},[394,752,621],{"class":400},[394,754,624],{"class":404},[394,756,757],{"class":462}," \u003C",[394,759,760],{"class":404},"releas",[394,762,763],{"class":458},"e",[394,765,553],{"class":462},[394,767,629],{"class":404},[394,769,409],{"class":408},[394,771,757],{"class":462},[394,773,774],{"class":404},"namespac",[394,776,763],{"class":458},[394,778,553],{"class":462},[394,780,782],{"class":781},"sxsTv"," \\\n",[394,784,785,788,791,793,795],{"class":396,"line":679},[394,786,787],{"class":408},"  --values",[394,789,790],{"class":404}," values-production.yaml",[394,792,642],{"class":408},[394,794,645],{"class":408},[394,796,797],{"class":404}," 30m\n",[14,799,800,801,804,805,807],{},"Follow it with ",[45,802,803],{},"kubectl rollout status",", one Deployment at a time, or on\n",[50,806,339],{},":",[385,809,811],{"className":387,"code":810,"language":389,"meta":390,"style":390},"kubectl rollout status deployment\u002F\u003Crelease>-tabularia-gateway --namespace \u003Cnamespace>\nkubectl rollout status deployment\u002F\u003Crelease>-tabularia-orchestrator --namespace \u003Cnamespace>\nkubectl rollout status deployment\u002F\u003Crelease>-tabularia-worker --namespace \u003Cnamespace> --timeout 70m\n",[45,812,813,849,880],{"__ignoreMap":390},[394,814,815,817,820,823,826,829,831,833,835,838,840,842,844,846],{"class":396,"line":397},[394,816,401],{"class":400},[394,818,819],{"class":404}," rollout",[394,821,822],{"class":404}," status",[394,824,825],{"class":404}," deployment\u002F",[394,827,828],{"class":462},"\u003C",[394,830,760],{"class":404},[394,832,763],{"class":458},[394,834,553],{"class":462},[394,836,837],{"class":404},"-tabularia-gateway",[394,839,409],{"class":408},[394,841,757],{"class":462},[394,843,774],{"class":404},[394,845,763],{"class":458},[394,847,848],{"class":462},">\n",[394,850,851,853,855,857,859,861,863,865,867,870,872,874,876,878],{"class":396,"line":679},[394,852,401],{"class":400},[394,854,819],{"class":404},[394,856,822],{"class":404},[394,858,825],{"class":404},[394,860,828],{"class":462},[394,862,760],{"class":404},[394,864,763],{"class":458},[394,866,553],{"class":462},[394,868,869],{"class":404},"-tabularia-orchestrator",[394,871,409],{"class":408},[394,873,757],{"class":462},[394,875,774],{"class":404},[394,877,763],{"class":458},[394,879,848],{"class":462},[394,881,883,885,887,889,891,893,895,897,899,902,904,906,908,910,912,914],{"class":396,"line":882},3,[394,884,401],{"class":400},[394,886,819],{"class":404},[394,888,822],{"class":404},[394,890,825],{"class":404},[394,892,828],{"class":462},[394,894,760],{"class":404},[394,896,763],{"class":458},[394,898,553],{"class":462},[394,900,901],{"class":404},"-tabularia-worker",[394,903,409],{"class":408},[394,905,757],{"class":462},[394,907,774],{"class":404},[394,909,763],{"class":458},[394,911,553],{"class":462},[394,913,645],{"class":408},[394,915,916],{"class":404}," 70m\n",[14,918,919,920,923,924,926,927,930],{},"The workers can take up to their grace period (one hour) to finish the runs in progress:\ngive their ",[45,921,922],{},"rollout status"," a longer timeout, or ",[45,925,47],{}," a ",[45,928,929],{},"--timeout"," longer than\nthe longest run.",[14,932,933,934,936],{},"What the migration did is in the log of the ",[45,935,222],{}," init container of a new pod, and the\nrevision can be read at any moment:",[385,938,940],{"className":387,"code":939,"language":389,"meta":390,"style":390},"kubectl logs deployment\u002F\u003Crelease>-tabularia-gateway -c migrate --namespace \u003Cnamespace>\nkubectl exec deployment\u002F\u003Crelease>-tabularia-gateway -c gateway --namespace \u003Cnamespace> -- python -m app.db.migra stato\n",[45,941,942,976],{"__ignoreMap":390},[394,943,944,946,949,951,953,955,957,959,961,963,966,968,970,972,974],{"class":396,"line":397},[394,945,401],{"class":400},[394,947,948],{"class":404}," logs",[394,950,825],{"class":404},[394,952,828],{"class":462},[394,954,760],{"class":404},[394,956,763],{"class":458},[394,958,553],{"class":462},[394,960,837],{"class":404},[394,962,693],{"class":408},[394,964,965],{"class":404}," migrate",[394,967,409],{"class":408},[394,969,757],{"class":462},[394,971,774],{"class":404},[394,973,763],{"class":458},[394,975,848],{"class":462},[394,977,978,980,982,984,986,988,990,992,994,996,998,1000,1002,1004,1006,1008,1010,1012,1014,1016],{"class":396,"line":679},[394,979,401],{"class":400},[394,981,405],{"class":404},[394,983,825],{"class":404},[394,985,828],{"class":462},[394,987,760],{"class":404},[394,989,763],{"class":458},[394,991,553],{"class":462},[394,993,837],{"class":404},[394,995,693],{"class":408},[394,997,696],{"class":404},[394,999,409],{"class":408},[394,1001,757],{"class":462},[394,1003,774],{"class":404},[394,1005,763],{"class":458},[394,1007,553],{"class":462},[394,1009,418],{"class":408},[394,1011,701],{"class":404},[394,1013,704],{"class":408},[394,1015,707],{"class":404},[394,1017,710],{"class":404},[14,1019,1020,1021,1024,1025,1028],{},"The log ends with ",[45,1022,1023],{},"database at revision 0021 (was 0020)"," when a migration ran, and with\n",[45,1026,1027],{},"database at revision 0021"," when another pod had already done it.",[14,1030,1031,1034],{},[50,1032,1033],{},"GitOps"," (Argo CD, Flux): nothing to change. The init container is idempotent, and a sync\nthat rolls the pods twice migrates once.",[370,1036,1038],{"id":1037},"migrations-in-your-own-pipeline","Migrations in your own pipeline",[14,1040,1041,1042,1045,1046,1048],{},"To migrate in a step of your own (a pipeline job, a change window), set\n",[45,1043,1044],{},"migrations.enabled: false"," and run the same command with the new image before\n",[45,1047,47],{},". The pods then only check the revision. A Job that does it, reading the\nrelease's ConfigMap and Secret:",[385,1050,1054],{"className":1051,"code":1052,"language":1053,"meta":390,"style":390},"language-yaml shiki shiki-themes github-dark-dimmed github-light","apiVersion: batch\u002Fv1\nkind: Job\nmetadata: { name: tabularia-migrate-1-3-0, namespace: \u003Cnamespace> }\nspec:\n  backoffLimit: 0\n  template:\n    spec:\n      restartPolicy: Never\n      containers:\n        - name: migrate\n          image: \u003Cregistry>\u002Ftabularia-gateway:1.3.0\n          command: [\"python\", \"-m\", \"app.db.migra\"]\n          envFrom:\n            - configMapRef: { name: \u003Crelease>-tabularia-config }\n            - secretRef: { name: \u003Crelease>-tabularia-secrets }\n","yaml",[45,1055,1056,1068,1078,1107,1116,1127,1135,1143,1154,1162,1175,1186,1211,1219,1239],{"__ignoreMap":390},[394,1057,1058,1062,1065],{"class":396,"line":397},[394,1059,1061],{"class":1060},"shb1k","apiVersion",[394,1063,1064],{"class":458},": ",[394,1066,1067],{"class":404},"batch\u002Fv1\n",[394,1069,1070,1073,1075],{"class":396,"line":679},[394,1071,1072],{"class":1060},"kind",[394,1074,1064],{"class":458},[394,1076,1077],{"class":404},"Job\n",[394,1079,1080,1083,1086,1089,1091,1094,1096,1099,1101,1104],{"class":396,"line":882},[394,1081,1082],{"class":1060},"metadata",[394,1084,1085],{"class":458},": { ",[394,1087,1088],{"class":1060},"name",[394,1090,1064],{"class":458},[394,1092,1093],{"class":404},"tabularia-migrate-1-3-0",[394,1095,209],{"class":458},[394,1097,1098],{"class":1060},"namespace",[394,1100,1064],{"class":458},[394,1102,1103],{"class":404},"\u003Cnamespace>",[394,1105,1106],{"class":458}," }\n",[394,1108,1110,1113],{"class":396,"line":1109},4,[394,1111,1112],{"class":1060},"spec",[394,1114,1115],{"class":458},":\n",[394,1117,1119,1122,1124],{"class":396,"line":1118},5,[394,1120,1121],{"class":1060},"  backoffLimit",[394,1123,1064],{"class":458},[394,1125,1126],{"class":408},"0\n",[394,1128,1130,1133],{"class":396,"line":1129},6,[394,1131,1132],{"class":1060},"  template",[394,1134,1115],{"class":458},[394,1136,1138,1141],{"class":396,"line":1137},7,[394,1139,1140],{"class":1060},"    spec",[394,1142,1115],{"class":458},[394,1144,1146,1149,1151],{"class":396,"line":1145},8,[394,1147,1148],{"class":1060},"      restartPolicy",[394,1150,1064],{"class":458},[394,1152,1153],{"class":404},"Never\n",[394,1155,1157,1160],{"class":396,"line":1156},9,[394,1158,1159],{"class":1060},"      containers",[394,1161,1115],{"class":458},[394,1163,1165,1168,1170,1172],{"class":396,"line":1164},10,[394,1166,1167],{"class":458},"        - ",[394,1169,1088],{"class":1060},[394,1171,1064],{"class":458},[394,1173,1174],{"class":404},"migrate\n",[394,1176,1178,1181,1183],{"class":396,"line":1177},11,[394,1179,1180],{"class":1060},"          image",[394,1182,1064],{"class":458},[394,1184,1185],{"class":404},"\u003Cregistry>\u002Ftabularia-gateway:1.3.0\n",[394,1187,1189,1192,1195,1198,1200,1203,1205,1208],{"class":396,"line":1188},12,[394,1190,1191],{"class":1060},"          command",[394,1193,1194],{"class":458},": [",[394,1196,1197],{"class":404},"\"python\"",[394,1199,209],{"class":458},[394,1201,1202],{"class":404},"\"-m\"",[394,1204,209],{"class":458},[394,1206,1207],{"class":404},"\"app.db.migra\"",[394,1209,1210],{"class":458},"]\n",[394,1212,1214,1217],{"class":396,"line":1213},13,[394,1215,1216],{"class":1060},"          envFrom",[394,1218,1115],{"class":458},[394,1220,1222,1225,1228,1230,1232,1234,1237],{"class":396,"line":1221},14,[394,1223,1224],{"class":458},"            - ",[394,1226,1227],{"class":1060},"configMapRef",[394,1229,1085],{"class":458},[394,1231,1088],{"class":1060},[394,1233,1064],{"class":458},[394,1235,1236],{"class":404},"\u003Crelease>-tabularia-config",[394,1238,1106],{"class":458},[394,1240,1242,1244,1247,1249,1251,1253,1256],{"class":396,"line":1241},15,[394,1243,1224],{"class":458},[394,1245,1246],{"class":1060},"secretRef",[394,1248,1085],{"class":458},[394,1250,1088],{"class":1060},[394,1252,1064],{"class":458},[394,1254,1255],{"class":404},"\u003Crelease>-tabularia-secrets",[394,1257,1106],{"class":458},[14,1259,1260,1261,1264],{},"It needs only the database's address and credentials, which the release already holds.\nWait for it (",[45,1262,1263],{},"kubectl wait --for=condition=complete job\u002Ftabularia-migrate-1-3-0","), then\nupgrade.",[58,1266,1268],{"id":1267},"when-something-goes-wrong","When something goes wrong",[14,1270,1271,1281],{},[50,1272,1273,1274,1277,1278,27],{},"A new gateway or orchestrator pod stays in ",[45,1275,1276],{},"Init:Error"," or ",[45,1279,1280],{},"Init:CrashLoopBackOff","\nThe migration failed; read why:",[385,1283,1285],{"className":387,"code":1284,"language":389,"meta":390,"style":390},"kubectl logs \u003Cthe new pod> -c migrate --namespace \u003Cnamespace>\n",[45,1286,1287],{"__ignoreMap":390},[394,1288,1289,1291,1293,1295,1298,1301,1304,1307,1309,1311,1313,1315,1317,1319,1321],{"class":396,"line":397},[394,1290,401],{"class":400},[394,1292,948],{"class":404},[394,1294,757],{"class":462},[394,1296,1297],{"class":404},"the",[394,1299,1300],{"class":404}," new",[394,1302,1303],{"class":404}," po",[394,1305,1306],{"class":458},"d",[394,1308,553],{"class":462},[394,1310,693],{"class":408},[394,1312,965],{"class":404},[394,1314,409],{"class":408},[394,1316,757],{"class":462},[394,1318,774],{"class":404},[394,1320,763],{"class":458},[394,1322,848],{"class":462},[14,1324,1325,1326,1328],{},"Nothing was half done: the migration is one transaction, and a failure rolls it back.\nThe old pods keep serving (",[45,1327,212],{},"), so Tabularia still works on the previous\nrelease. Common causes: the database is unreachable, or its user may not change the\nschema (the migration needs the rights of the tables' owner). Fix the cause; the pod\nretries by itself. To give up instead, roll back (below).",[14,1330,1331,1338,1339,364,1342,1345,1346,1349,1350,1352],{},[50,1332,1333,1334,1337],{},"A new pod stays ",[45,1335,1336],{},"0\u002F1"," or restarts."," ",[45,1340,1341],{},"kubectl describe pod",[45,1343,1344],{},"kubectl logs"," say why.\nA gateway that says ",[310,1347,1348],{},"the database is at revision … but this version of the gateway\nneeds …"," found the database behind its code: the migration did not run\n(",[45,1351,1044],{}," without your own step).",[14,1354,1355],{},[50,1356,1357],{},"Roll back to the previous release.",[385,1359,1361],{"className":387,"code":1360,"language":389,"meta":390,"style":390},"helm history tabularia --namespace tabularia\nhelm rollback tabularia \u003Cthe revision before> --namespace tabularia --wait --timeout 20m\n",[45,1362,1363,1376],{"__ignoreMap":390},[394,1364,1365,1367,1370,1372,1374],{"class":396,"line":397},[394,1366,621],{"class":400},[394,1368,1369],{"class":404}," history",[394,1371,427],{"class":404},[394,1373,409],{"class":408},[394,1375,442],{"class":404},[394,1377,1378,1380,1383,1385,1387,1389,1392,1395,1397,1399,1401,1403,1405,1407],{"class":396,"line":679},[394,1379,621],{"class":400},[394,1381,1382],{"class":404}," rollback",[394,1384,427],{"class":404},[394,1386,757],{"class":462},[394,1388,1297],{"class":404},[394,1390,1391],{"class":404}," revision",[394,1393,1394],{"class":404}," befor",[394,1396,763],{"class":458},[394,1398,553],{"class":462},[394,1400,409],{"class":408},[394,1402,427],{"class":404},[394,1404,642],{"class":408},[394,1406,645],{"class":408},[394,1408,648],{"class":404},[14,1410,1411,1412,1414,1415,1418,1419,1422,1423,1425],{},"The pods go back to the previous images and values; the database stays migrated, which\nthe previous release accepts: its ",[45,1413,222],{}," step finds a revision it does not know, does\nnothing, and the gateway starts with a warning that the database is newer than its code.\nThis holds for ",[50,1416,1417],{},"one release back",", and ",[50,1420,1421],{},"from 1.2.2 on",": 1.2.1 and earlier stop on a\ndatabase migrated by a newer release (their new pods stay in ",[45,1424,1280],{},", and\nthe release is left half rolled back), so going back to them means restoring the backup\ntaken before the upgrade, below. Further back, or when the data itself is in question,\nrestore the backup too.",[14,1427,1428,1431,1432,1434,1435,1437,1438,1441,1442,1445,1446,1448,1449,807],{},[50,1429,1430],{},"Restore the backup."," First the file: the one of step 1, or one of the automatic\nbackups — ",[50,1433,382],{}," in Administration → Backups while Tabularia still works, otherwise\nstraight from the bucket, in your provider's console or with its tools (the files are\nunder ",[45,1436,123],{},", named after the time they were made, in UTC). Tabularia makes\nthem with the ",[45,1439,1440],{},"pg_dump"," of the server's own version, so the server's own ",[45,1443,1444],{},"pg_restore","\nreads them; a newer ",[45,1447,1444],{}," does too, an older one does not. Then on minikube, with\nthe file in the current folder as ",[45,1450,535],{},[32,1452,1453,1515,1542,1651],{},[35,1454,1455,1456,1511,1514],{},"Stop everything that uses the database (the HorizontalPodAutoscalers and KEDA, if you\nturned them on, would start the pods again: roll back first, see point 4):",[385,1457,1459],{"className":387,"code":1458,"language":389,"meta":390,"style":390},"kubectl scale deployment --namespace tabularia --replicas 0 -l app.kubernetes.io\u002Finstance=tabularia\nkubectl wait --for=delete pod --namespace tabularia -l app.kubernetes.io\u002Finstance=tabularia --timeout 70m\n",[45,1460,1461,1486],{"__ignoreMap":390},[394,1462,1463,1465,1468,1471,1473,1475,1478,1481,1483],{"class":396,"line":397},[394,1464,401],{"class":400},[394,1466,1467],{"class":404}," scale",[394,1469,1470],{"class":404}," deployment",[394,1472,409],{"class":408},[394,1474,427],{"class":404},[394,1476,1477],{"class":408}," --replicas",[394,1479,1480],{"class":408}," 0",[394,1482,481],{"class":408},[394,1484,1485],{"class":404}," app.kubernetes.io\u002Finstance=tabularia\n",[394,1487,1488,1490,1493,1496,1498,1500,1502,1504,1507,1509],{"class":396,"line":679},[394,1489,401],{"class":400},[394,1491,1492],{"class":404}," wait",[394,1494,1495],{"class":408}," --for=delete",[394,1497,474],{"class":404},[394,1499,409],{"class":408},[394,1501,427],{"class":404},[394,1503,481],{"class":408},[394,1505,1506],{"class":404}," app.kubernetes.io\u002Finstance=tabularia",[394,1508,645],{"class":408},[394,1510,916],{"class":404},[1512,1513],"br",{},"The second command returns when the last pod is gone: a worker finishing a run can\ntake up to its grace period.",[35,1516,1517,1518,1520,1521],{},"Copy the backup into the PostgreSQL pod (with ",[45,1519,459],{}," set as in step 1):",[385,1522,1524],{"className":387,"code":1523,"language":389,"meta":390,"style":390},"kubectl cp tabularia-backup.dump \"tabularia-deps\u002F${POD}:\u002Ftmp\u002Frestore.dump\"\n",[45,1525,1526],{"__ignoreMap":390},[394,1527,1528,1530,1532,1535,1537,1539],{"class":396,"line":397},[394,1529,401],{"class":400},[394,1531,519],{"class":404},[394,1533,1534],{"class":404}," tabularia-backup.dump",[394,1536,522],{"class":404},[394,1538,459],{"class":458},[394,1540,1541],{"class":404},"}:\u002Ftmp\u002Frestore.dump\"\n",[35,1543,1544,1545,1644,1646,1647,1650],{},"Recreate the database empty and load the backup. Recreating it, rather than restoring\nover it, also removes the tables the newer release had added:",[385,1546,1548],{"className":387,"code":1547,"language":389,"meta":390,"style":390},"kubectl exec --namespace tabularia-deps deploy\u002Fpostgres -- dropdb --username tabularia --force tabularia\nkubectl exec --namespace tabularia-deps deploy\u002Fpostgres -- createdb --username tabularia tabularia\nkubectl exec --namespace tabularia-deps deploy\u002Fpostgres -- pg_restore --username tabularia --dbname tabularia \\\n  --no-owner --no-privileges --single-transaction --exit-on-error \u002Ftmp\u002Frestore.dump\n",[45,1549,1550,1576,1599,1627],{"__ignoreMap":390},[394,1551,1552,1554,1556,1558,1560,1562,1564,1567,1569,1571,1574],{"class":396,"line":397},[394,1553,401],{"class":400},[394,1555,405],{"class":404},[394,1557,409],{"class":408},[394,1559,412],{"class":404},[394,1561,415],{"class":404},[394,1563,418],{"class":408},[394,1565,1566],{"class":404}," dropdb",[394,1568,424],{"class":408},[394,1570,427],{"class":404},[394,1572,1573],{"class":408}," --force",[394,1575,442],{"class":404},[394,1577,1578,1580,1582,1584,1586,1588,1590,1593,1595,1597],{"class":396,"line":679},[394,1579,401],{"class":400},[394,1581,405],{"class":404},[394,1583,409],{"class":408},[394,1585,412],{"class":404},[394,1587,415],{"class":404},[394,1589,418],{"class":408},[394,1591,1592],{"class":404}," createdb",[394,1594,424],{"class":408},[394,1596,427],{"class":404},[394,1598,442],{"class":404},[394,1600,1601,1603,1605,1607,1609,1611,1613,1616,1618,1620,1623,1625],{"class":396,"line":882},[394,1602,401],{"class":400},[394,1604,405],{"class":404},[394,1606,409],{"class":408},[394,1608,412],{"class":404},[394,1610,415],{"class":404},[394,1612,418],{"class":408},[394,1614,1615],{"class":404}," pg_restore",[394,1617,424],{"class":408},[394,1619,427],{"class":404},[394,1621,1622],{"class":408}," --dbname",[394,1624,427],{"class":404},[394,1626,782],{"class":781},[394,1628,1629,1632,1635,1638,1641],{"class":396,"line":1109},[394,1630,1631],{"class":408},"  --no-owner",[394,1633,1634],{"class":408}," --no-privileges",[394,1636,1637],{"class":408}," --single-transaction",[394,1639,1640],{"class":408}," --exit-on-error",[394,1642,1643],{"class":404}," \u002Ftmp\u002Frestore.dump\n",[1512,1645],{},"All or nothing: the first error stops it and leaves the database empty. If it prints\nan error, or ends with ",[310,1648,1649],{},"errors ignored on restore",", or its exit status is not 0, do not\nstart the release: an empty database would be set up again as a new installation.",[35,1652,1653,1654,1657,1658,1660],{},"Start the release the backup belongs to: ",[45,1655,1656],{},"helm rollback"," to its revision (which also\nsets the number of pods back), or ",[45,1659,47],{}," from that release's folder.",[14,1662,1663],{},"On a cluster, the same steps with your database's own restore in point 3.",[58,1665,1667],{"id":1666},"postgresql-in-the-cluster","PostgreSQL in the cluster",[14,1669,1670],{},"When PostgreSQL runs in the cluster, as on minikube and kind, its volume is what holds\nTabularia's records:",[198,1672,1673,1687,1700,1710],{},[35,1674,1675,1678,1679,1682,1683,1686],{},[50,1676,1677],{},"One pod at a time."," Its Deployment uses ",[45,1680,1681],{},"strategy: Recreate"," and a ",[45,1684,1685],{},"ReadWriteOnce","\nvolume: the old pod stops before the new one starts. Two PostgreSQL servers on the\nsame data directory would corrupt it.",[35,1688,1689,1692,1693,1696,1697,27],{},[50,1690,1691],{},"The volume outlives the pod, not the namespace."," Deleting the PersistentVolumeClaim,\nor the namespace that holds it, deletes the data when the storage class says\n",[45,1694,1695],{},"reclaimPolicy: Delete"," (the default of most, minikube's and kind's included). See it\nwith ",[45,1698,1699],{},"kubectl get pv",[35,1701,1702,1705,1706,1709],{},[50,1703,1704],{},"A new minor PostgreSQL version"," (16.4 → 16.8) needs nothing: the same image tag\n(",[45,1707,1708],{},"postgres:16-alpine",") restarted picks it up, on the same files.",[35,1711,1712,1715],{},[50,1713,1714],{},"A new major version"," (16 → 17) does not read the old files: back up, delete the\nDeployment and the PersistentVolumeClaim, apply the new image with an empty volume,\nand restore the backup into it as above.",[14,1717,1718],{},"Valkey has no volume in these setups, on purpose: what it holds can be lost (queued\ntasks, the cache index), and the workers rebuild it.",[1720,1721,1722],"style",{},"html pre.shiki code .sqRhv, html code.shiki .sqRhv{--shiki-default:#F69D50;--shiki-light:#6F42C1}html pre.shiki code .sXfbr, html code.shiki .sXfbr{--shiki-default:#96D0FF;--shiki-light:#032F62}html pre.shiki code .swcJU, html code.shiki .swcJU{--shiki-default:#6CB6FF;--shiki-light:#005CC5}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 .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 pre.shiki code .ssh_m, html code.shiki .ssh_m{--shiki-default:#ADBAC7;--shiki-light:#24292E}html pre.shiki code .s6PUj, html code.shiki .s6PUj{--shiki-default:#F47067;--shiki-light:#D73A49}html pre.shiki code .sxsTv, html code.shiki .sxsTv{--shiki-default:#F47067;--shiki-light:#005CC5}html pre.shiki code .shb1k, html code.shiki .shb1k{--shiki-default:#8DDB8C;--shiki-light:#22863A}",{"title":390,"searchDepth":679,"depth":882,"links":1724},[1725,1726,1728,1729,1735,1738,1739],{"id":60,"depth":679,"text":61},{"id":188,"depth":679,"text":1727},"What happens during helm upgrade",{"id":276,"depth":679,"text":277},{"id":343,"depth":679,"text":344,"children":1730},[1731,1732,1733,1734],{"id":372,"depth":882,"text":373},{"id":557,"depth":882,"text":558},{"id":610,"depth":882,"text":611},{"id":657,"depth":882,"text":658},{"id":735,"depth":679,"text":736,"children":1736},[1737],{"id":1037,"depth":882,"text":1038},{"id":1267,"depth":679,"text":1268},{"id":1666,"depth":679,"text":1667},"How to move an installation made with the Helm chart to a newer release: what an upgrade\nchanges and what it keeps, how the database schema is migrated, how the new pods replace\nthe old ones, and what to do when something goes wrong. The first part applies to every\ninstallation; then come the steps on minikube and\non a Kubernetes cluster.","md",{"source":1743},"docs\u002Fdeploy\u002Fupgrading.md",true,"\u002Fdocs\u002Fdeploy\u002Fupgrading",{"title":5,"description":1740},"docs\u002Fdeploy\u002Fupgrading","62d5qvEim-uuTVxxrIYZa9svVgSc4k7qQJRN_g5DZMg",[1750,1768,1824,1856,1888,1917,1943,1967],{"title":1751,"icon":1752,"intro":1753,"items":1754},"Getting started","start","Install it on one machine, learn the few ideas everything else is built on, and run a first flow.",[1755,1757,1760,1762,1765],{"path":1756,"title":1751},"\u002Fdocs\u002Fgetting-started",{"path":1758,"title":1759},"\u002Fdocs\u002Fgetting-started-docker-compose","Walkthrough with Docker Compose",{"path":350,"title":1761},"Try it on your computer (minikube)",{"path":1763,"title":1764},"\u002Fdocs\u002Fconcepts","Concepts",{"path":1766,"title":1767},"\u002Fdocs\u002Foverview","Overview (README)",{"title":1769,"icon":1770,"intro":1771,"items":1772},"Capabilities","capabilities","What you can build: visual flows, analysis on any step, AI questions, pixel-perfect reports, dashboards, Python, R and SQL scripts, containers and the orchestration that ties them together.",[1773,1776,1779,1782,1785,1788,1791,1794,1797,1800,1803,1806,1809,1812,1815,1818,1821],{"path":1774,"title":1775},"\u002Fdocs\u002Fcapabilities","All capabilities",{"path":1777,"title":1778},"\u002Fdocs\u002Fcapabilities\u002Fflows","Low-code flows",{"path":1780,"title":1781},"\u002Fdocs\u002Fcapabilities\u002Fpreview-and-analysis","Preview and analysis",{"path":1783,"title":1784},"\u002Fdocs\u002Fcapabilities\u002Fask-your-data","Ask your data with AI",{"path":1786,"title":1787},"\u002Fdocs\u002Fcapabilities\u002Freports","Pixel-perfect reports",{"path":1789,"title":1790},"\u002Fdocs\u002Fcapabilities\u002Freport-functions","Report functions",{"path":1792,"title":1793},"\u002Fdocs\u002Fcapabilities\u002Fdashboards","Dashboards",{"path":1795,"title":1796},"\u002Fdocs\u002Fscripting","Python and R",{"path":1798,"title":1799},"\u002Fdocs\u002Fsql-scripts","SQL scripts",{"path":1801,"title":1802},"\u002Fdocs\u002Fcapabilities\u002Fnotebooks","marimo notebooks as apps",{"path":1804,"title":1805},"\u002Fdocs\u002Fcontainer-node","Docker images in flows",{"path":1807,"title":1808},"\u002Fdocs\u002Fcapabilities\u002Fflow-groups","Flow groups: orchestration",{"path":1810,"title":1811},"\u002Fdocs\u002Fcapabilities\u002Fscheduling","Scheduling",{"path":1813,"title":1814},"\u002Fdocs\u002Fpushmail","Pushmail: datasources, reports and dashboards by email",{"path":1816,"title":1817},"\u002Fdocs\u002Fplaceholders","Dynamic dates",{"path":1819,"title":1820},"\u002Fdocs\u002Fcapabilities\u002Fengines","Engines",{"path":1822,"title":1823},"\u002Fdocs\u002Fengines\u002Fengine-differences","Engine differences",{"title":1825,"icon":1826,"intro":1827,"items":1828},"Sources","sources","Every way data comes in: files, databases, buckets, SFTP folders, HTTP APIs, SharePoint, Google Sheets, and the results of your own flows.",[1829,1832,1835,1838,1841,1844,1847,1850,1853],{"path":1830,"title":1831},"\u002Fdocs\u002Fsources","All sources",{"path":1833,"title":1834},"\u002Fdocs\u002Fsources\u002Ffiles","Uploaded files",{"path":1836,"title":1837},"\u002Fdocs\u002Fsources\u002Fdatabases","Database tables and queries",{"path":1839,"title":1840},"\u002Fdocs\u002Fs3-datasource","S3 buckets",{"path":1842,"title":1843},"\u002Fdocs\u002Fsftp","SFTP folders",{"path":1845,"title":1846},"\u002Fdocs\u002Fapi-datasource","HTTP APIs",{"path":1848,"title":1849},"\u002Fdocs\u002Fsources\u002Fsharepoint","Excel on SharePoint",{"path":1851,"title":1852},"\u002Fdocs\u002Fsources\u002Fgoogle-sheets","Google Sheets",{"path":1854,"title":1855},"\u002Fdocs\u002Fsources\u002Fflow-outputs","Results of flows and scripts",{"title":1857,"icon":1858,"intro":1859,"items":1860},"Connections","connections","The systems Tabularia reads from and writes to, how each connection is set up, and who may use it.",[1861,1864,1867,1870,1873,1875,1876,1878,1879,1882,1885],{"path":1862,"title":1863},"\u002Fdocs\u002Fconnections","All connections",{"path":1865,"title":1866},"\u002Fdocs\u002Fconnections\u002Fdatabases","Databases",{"path":1868,"title":1869},"\u002Fdocs\u002Fsqlserver","Microsoft SQL Server driver",{"path":1871,"title":1872},"\u002Fdocs\u002Fconnections\u002Fs3","S3 and compatible storage",{"path":1842,"title":1874},"SFTP servers",{"path":1845,"title":1846},{"path":1848,"title":1877},"SharePoint",{"path":1851,"title":1852},{"path":1880,"title":1881},"\u002Fdocs\u002Fconnections\u002Fsmtp","SMTP email",{"path":1883,"title":1884},"\u002Fdocs\u002Fcontainer-registries","Container registries",{"path":1886,"title":1887},"\u002Fdocs\u002Fconnections\u002Fdestinations","Writing data out",{"title":1889,"icon":1890,"intro":1891,"items":1892},"Integrations","integrations","How Tabularia fits your stack: AI providers, the REST API and webhooks, dbt, OpenLineage, single sign-on, SIEM and monitoring.",[1893,1896,1899,1902,1905,1908,1911,1914],{"path":1894,"title":1895},"\u002Fdocs\u002Fintegrations","All integrations",{"path":1897,"title":1898},"\u002Fdocs\u002Fintegrations\u002Fai","AI: where it is used",{"path":1900,"title":1901},"\u002Fdocs\u002Fapi","API, tokens and webhooks",{"path":1903,"title":1904},"\u002Fdocs\u002Fdbt-export","Export to dbt",{"path":1906,"title":1907},"\u002Fdocs\u002Fopenlineage","OpenLineage",{"path":1909,"title":1910},"\u002Fdocs\u002Fsso-keycloak","Single sign-on with Keycloak",{"path":1912,"title":1913},"\u002Fdocs\u002Fsiem-splunk","Audit log to Splunk",{"path":1915,"title":1916},"\u002Fdocs\u002Fintegrations\u002Fmonitoring","Monitoring",{"title":1918,"icon":1919,"intro":1920,"items":1921},"Security & governance","governance","Who may see and do what, what is recorded, and how data quality and personal data are kept in check.",[1922,1925,1928,1931,1934,1937,1940],{"path":1923,"title":1924},"\u002Fdocs\u002Fgovernance","Overview",{"path":1926,"title":1927},"\u002Fdocs\u002Fgovernance\u002Fpermissions","Permissions",{"path":1929,"title":1930},"\u002Fdocs\u002Fgovernance\u002Faudit-log","Audit log",{"path":1932,"title":1933},"\u002Fdocs\u002Fgovernance\u002Fdata-contracts","Data contracts",{"path":1935,"title":1936},"\u002Fdocs\u002Fdesign\u002Fsso-group-mapping","SSO and group mapping",{"path":1938,"title":1939},"\u002Fdocs\u002Fexamples\u002Fkeycloak","Keycloak example on a laptop",{"path":1941,"title":1942},"\u002Fdocs\u002Fengines\u002Fclickhouse-ai-user","A ClickHouse user for the AI assistant",{"title":1944,"icon":1945,"intro":1946,"items":1947},"Install & operate","operate","Run it in production on Kubernetes, configure it, upgrade it and keep it healthy.",[1948,1951,1954,1956,1959,1962,1965],{"path":1949,"title":1950},"\u002Fdocs\u002Fdeploy\u002Fkubernetes","Kubernetes and Helm",{"path":1952,"title":1953},"\u002Fdocs\u002Fdeploy\u002Fkind","A company-like cluster on one computer (kind)",{"path":1745,"title":1955},"Upgrading, backups and restore",{"path":1957,"title":1958},"\u002Fdocs\u002Fdeploy\u002Frelease-checklist","Install and upgrade checklist",{"path":1960,"title":1961},"\u002Fdocs\u002Fconfiguration","Configuration reference",{"path":1963,"title":1964},"\u002Fdocs\u002Fengine-settings","Settings from the panel",{"path":236,"title":1966},"Database migrations",{"title":1968,"icon":1969,"intro":1970,"items":1971},"Reference","reference","Release process and the remaining reference pages.",[1972],{"path":1973,"title":1974},"\u002Fdocs\u002Frelease-process","Release process",1793656372232]