PageSourceSearch

https://www.vcluster.com/docs/assets/js/47c3044b.c106c1d3.js

js vcluster.com collected 2026-09-24 19:07:54 UTC 18,204 bytes, 79 lines download raw bytes

1"use strict";(self.webpackChunkvcluster_docs=self.webpackChunkvcluster_docs||[]).push([["14173"],{94450(e,t,r){r.r(t),r.d(t,{metadata:()=>a,default:()=>b,frontMatter:()=>p,contentTitle:()=>u,toc:()=>m,assets:()=>h});var a=JSON.parse('{"id":"administer/clusters/advanced/multi-region/restore","title":"Restore the Kine Database from a Snapshot","description":"Runbook for restoring the shared Kine database from an RDS snapshot and updating both platform regions to use the restored instance.","source":"@site/platform_versioned_docs/version-4.12.0/administer/clusters/advanced/multi-region/restore.mdx","sourceDirName":"administer/clusters/advanced/multi-region","slug":"/administer/clusters/advanced/multi-region/restore","permalink":"/docs/platform/administer/clusters/advanced/multi-region/restore","draft":false,"unlisted":false,"editUrl":"https://github.com/loft-sh/vcluster-docs/edit/main/platform_versioned_docs/version-4.12.0/administer/clusters/advanced/multi-region/restore.mdx","tags":[],"version":"4.12.0","sidebarPosition":4,"frontMatter":{"sidebar_label":"Restore Database","sidebar_position":4,"title":"Restore the Kine Database from a Snapshot","description":"Runbook for restoring the shared Kine database from an RDS snapshot and updating both platform regions to use the restored instance.","toc_max_heading_level":3},"sidebar":"siteSidebar","previous":{"title":"Upgrade","permalink":"/docs/platform/administer/clusters/advanced/multi-region/upgrade"},"next":{"title":"Recover from Failover","permalink":"/docs/platform/administer/clusters/advanced/multi-region/recover"}}'),s=r(74848),o=r(28453),n=r(50773),i=r(57250),l=r(21472),d=r(16355),c=r(82187);let p={sidebar_label:"Restore Database",sidebar_position:4,title:"Restore the Kine Database from a Snapshot",description:"Runbook for restoring the shared Kine database from an RDS snapshot and updating both platform regions to use the restored instance.",toc_max_heading_level:3},u,h={},m=[{value:"Configure your values",id:"configure-your-values",level:2},{value:"Step 1 - Create a snapshot of the current database",id:"step-1---create-a-snapshot-of-the-current-database",level:2},{value:"Step 2 - Restore the snapshot to a new RDS instance",id:"step-2---restore-the-snapshot-to-a-new-rds-instance",level:2},{value:"Step 3 - Scale down both regions",id:"step-3---scale-down-both-regions",level:2},{value:"Step 4 - Update the values files",id:"step-4---update-the-values-files",level:2},{value:"Step 5 - Upgrade both regions",id:"step-5---upgrade-both-regions",level:2},{value:"Step 6 - Wait for both regions to come back up",id:"step-6---wait-for-both-regions-to-come-back-up",level:2},{value:"Step 7 - Verify the restore",id:"step-7---verify-the-restore",level:2}];function f(e){let t={admonition:"admonition",code:"code",h2:"h2",p:"p",...(0,o.R)(),...e.components},{Details:r}=t;return r||function(e,t){throw Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(l.A,{platformVersion:"v4.8.0"}),"\n",(0,s.jsx)(t.p,{children:"This runbook covers restoring the shared Kine database from an RDS snapshot to a\nnew instance and updating both platform regions to use it. Use this procedure for\ndisaster recovery, database migration (for example, enabling IAM authentication),\nor point-in-time recovery."}),"\n",(0,s.jsx)(t.admonition,{title:"Important",type:"warning",children:(0,s.jsx)(t.p,{children:"Both platform regions must be scaled down before switching the data source to\nprevent split-brain writes to the old and new databases."})}),"\n",(0,s.jsx)(t.h2,{id:"configure-your-values",children:"Configure your values"}),"\n",(0,s.jsx)(t.p,{children:"This runbook references AWS resource IDs, cluster context ARNs, and file names specific to your deployment. Set them below once and all commands update automatically."}),"\n",(0,s.jsxs)(r,{children:[(0,s.jsx)("summary",{children:"Expand to set page variables"}),(0,s.jsx)(c.A,{ACCOUNT_ID:"123456789012",AWS_REGION:"us-east-1",DB_SG_ID:"sg-xxxxxxxxx",FIRST_REGION_CONTEXT:"arn:aws:eks:us-east-1:123456789012:cluster/platform-multi-region-us-east-1",SECOND_REGION_CONTEXT:"arn:aws:eks:eu-west-1:123456789012:cluster/platform-multi-region-eu-west-1",SNAPSHOT_NAME:"kine-backup-YYYY-MM-DD",NEW_DB_INSTANCE_ID:"mariadb-multi-region-restored",FIRST_REGION_VALUES_FILE:"platform-us-east-1-values.yaml",SECOND_REGION_VALUES_FILE:"platform-eu-west-1-values.yaml"})]}),"\n",(0,s.jsx)(t.h2,{id:"step-1---create-a-snapshot-of-the-current-database",children:"Step 1 - Create a snapshot of the current database"}
1),"\n",(0,s.jsx)(t.p,{children:"Skip this step if you already have a snapshot to restore from."}),"\n",(0,s.jsx)(d.A,{code:`aws rds create-db-snapshot \\
2--db-instance-identifier [[VAR:CURRENT_DB_INSTANCE_ID:mariadb-multi-region]] \\
3--db-snapshot-identifier [[GLOBAL:SNAPSHOT_NAME]] \\
4--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsx)(t.p,{children:"Wait for the snapshot to become available:"}),"\n",(0,s.jsx)(d.A,{code:`aws rds wait db-snapshot-available \\
5--db-snapshot-identifier [[GLOBAL:SNAPSHOT_NAME]] \\
6--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsx)(t.h2,{id:"step-2---restore-the-snapshot-to-a-new-rds-instance",children:"Step 2 - Restore the snapshot to a new RDS instance"}),"\n",(0,s.jsxs)(t.p,{children:["Restore the snapshot to a new instance in the database VPC. Use the same DB\nsubnet group and security group from the original setup. Include\n",(0,s.jsx)(t.code,{children:"--enable-iam-database-authentication"})," if the new instance should use IAM auth."]}),"\n",(0,s.jsx)(d.A,{code:`aws rds restore-db-instance-from-db-snapshot \\
7--db-instance-identifier [[GLOBAL:NEW_DB_INSTANCE_ID]] \\
8--db-snapshot-identifier [[GLOBAL:SNAPSHOT_NAME]] \\
9--db-instance-class db.t3.medium \\
10--db-subnet-group-name multi-region-db-subnet \\
11--vpc-security-group-ids [[GLOBAL:DB_SG_ID]] \\
12--no-publicly-accessible \\
13--enable-iam-database-authentication \\
14--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsx)(t.p,{children:"Wait for the new instance to become available:"}),"\n",(0,s.jsx)(d.A,{code:`aws rds wait db-instance-available \\
15--db-instance-identifier [[GLOBAL:NEW_DB_INSTANCE_ID]] \\
16--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsx)(t.p,{children:"Note the new endpoint:"}),"\n",(0,s.jsx)(d.A,{code:`aws rds describe-db-instances \\
17--db-instance-identifier [[GLOBAL:NEW_DB_INSTANCE_ID]] \\
18--query 'DBInstances[0].Endpoint.Address' \\
19--output text \\
20--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsxs)(t.p,{children:["If using IAM authentication, note the ",(0,s.jsx)(t.code,{children:"DbiResourceId"})," of the new instance and\nupdate the ",(0,s.jsx)(t.code,{children:"RDSIAMAuthKine"})," IAM policy to include it. Without this, platform\npods fail with ",(0,s.jsx)(t.code,{children:"Access denied for user 'kine'"})," errors because the IAM\n",(0,s.jsx)(t.code,{children:"rds-db:connect"})," permission is scoped to a specific RDS instance resource ID."]}),"\n",(0,s.jsx)(d.A,{code:`aws rds describe-db-instances \\
21--db-instance-identifier [[GLOBAL:NEW_DB_INSTANCE_ID]] \\
22--query 'DBInstances[0].DbiResourceId' \\
23--output text \\
24--region [[GLOBAL:AWS_REGION]]`,language:"bash"}),"\n",(0,s.jsxs)(t.p,{children:["Add the new resource ID to the policy's ",(0,s.jsx)(t.code,{children:"Resource"})," array:"]}),"\n",(0,s.jsx)(d.A,{code:`aws iam create-policy-version \\
25--policy-arn arn:aws:iam::[[GLOBAL:ACCOUNT_ID]]:policy/RDSIAMAuthKine \\
26--set-as-default \\
27--policy-document '{
28  "Version": "2012-10-17",
29  "Statement": [
30    {
31      "Effect": "Allow",
32      "Action": "rds-db:connect",
33      "Resource": [
34        "arn:aws:rds-db:[[GLOBAL:AWS_REGION]]:[[GLOBAL:ACCOUNT_ID]]:dbuser:[[VAR:OLD_DBI_RESOURCE_ID:db-OLDXXXXXXXXXXXXXXXXXXXXXXXXXX]]/kine",
35        "arn:aws:rds-db:[[GLOBAL:AWS_REGION]]:[[GLOBAL:ACCOUNT_ID]]:dbuser:[[VAR:NEW_DBI_RESOURCE_ID:db-NEWXXXXXXXXXXXXXXXXXXXXXXXXXX]]/kine"
36      ]
37    }
38  ]
39}'`,language:"bash"}),"\n",(0,s.jsx)(t.h2,{id:"step-3---scale-down-both-regions",children:"Step 3 - Scale down both regions"}),"\n",(0,s.jsx)(t.p,{children:"Scale both platform deployments to zero to stop all writes to the old database."}),"\n",(0,s.jsx)(d.A,{code:`kubectl --context [[GLOBAL:FIRST_REGION_CONTEXT]] \\
40scale deployment -n vcluster-platform loft --replicas=0
41
42kubectl --context [[GLOBAL:SECOND_REGION_CONTEXT]] \\
43scale deployment -n vcluster-platform loft --replicas=0`,language:"bash"}),"\n",(0,s.jsx)(t.p,{children:"Wait for all pods to stop:"}),"\n",(0,s.jsx)(d.A,{code:`kubectl --context [[GLOBAL:FIRST_REGION_CONTEXT]] \\
44get pods -n vcluster-platform -l app=loft --watch
45
46kubectl --context [[GLOBAL:SECOND_REGION_CONTEXT]] \\
47get pods -n vcluster-platform -l app=loft --watch`,language:"bash"}),"\n",(0,s.jsx)(t.h2,{id:"step-4---update-the-values-files",children:"Step 4 - Upd
47ate the values files"}),"\n",(0,s.jsxs)(t.p,{children:["Update the ",(0,s.jsx)(t.code,{children:"dataSource"})," in both region values files to point to the new RDS\nendpoint:"]}),"\n",(0,s.jsx)(d.A,{code:'config:\n  database:\n    dataSource: "mysql://kine@tcp([[VAR:NEW_DATABASE_URL:mariadb-multi-region-restored.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com]]:3306)/kine"',language:"yaml"}),"\n",(0,s.jsx)(t.h2,{id:"step-5---upgrade-both-regions",children:"Step 5 - Upgrade both regions"}),"\n",(0,s.jsxs)(t.p,{children:["Apply the updated values files to both regions. The upgrade reapplies\n",(0,s.jsx)(t.code,{children:"replicaCount"})," from the values files, so this also brings both regions back up,\nnow pointing at the restored database."]}),"\n",(0,s.jsxs)(n.A,{defaultValue:"cli",values:[{label:"vCluster CLI",value:"cli"},{label:"Helm",value:"helm"}],children:[(0,s.jsx)(i.A,{value:"cli",children:(0,s.jsx)(d.A,{code:`vcluster platform start \\
48--namespace vcluster-platform \\
49--context [[GLOBAL:FIRST_REGION_CONTEXT]] \\
50--values [[GLOBAL:FIRST_REGION_VALUES_FILE]] \\
51--upgrade \\
52--no-tunnel
53
54vcluster platform start \\
55--namespace vcluster-platform \\
56--context [[GLOBAL:SECOND_REGION_CONTEXT]] \\
57--values [[GLOBAL:SECOND_REGION_VALUES_FILE]] \\
58--upgrade \\
59--no-tunnel`,language:"bash"})}),(0,s.jsx)(i.A,{value:"helm",children:(0,s.jsx)(d.A,{code:`helm upgrade loft vcluster-platform --install --create-namespace --repository-config='' \\
60--namespace vcluster-platform \\
61--repo "https://charts.loft.sh/" \\
62--version [[VAR:CHART_VERSION:__PLATFORM_VERSION__]] \\
63--kube-context [[GLOBAL:FIRST_REGION_CONTEXT]] \\
64-f [[GLOBAL:FIRST_REGION_VALUES_FILE]]
65
66helm upgrade loft vcluster-platform --install --create-namespace --repository-config='' \\
67--namespace vcluster-platform \\
68--repo "https://charts.loft.sh/" \\
69--version [[VAR:CHART_VERSION:__PLATFORM_VERSION__]] \\
70--kube-context [[GLOBAL:SECOND_REGION_CONTEXT]] \\
71-f [[GLOBAL:SECOND_REGION_VALUES_FILE]]`,language:"bash"})})]}),"\n",(0,s.jsx)(t.h2,{id:"step-6---wait-for-both-regions-to-come-back-up",children:"Step 6 - Wait for both regions to come back up"}),"\n",(0,s.jsx)(t.p,{children:"Wait for all pods to become ready:"}),"\n",(0,s.jsx)(d.A,{code:`kubectl --context [[GLOBAL:FIRST_REGION_CONTEXT]] \\
72rollout status deployment/loft -n vcluster-platform
73
74kubectl --context [[GLOBAL:SECOND_REGION_CONTEXT]] \\
75rollout status deployment/loft -n vcluster-platform`,language:"bash"}),"\n",(0,s.jsx)(t.h2,{id:"step-7---verify-the-restore",children:"Step 7 - Verify the restore"}),"\n",(0,s.jsx)(t.p,{children:"Confirm the platform is healthy on both regions:"}),"\n",(0,s.jsx)(d.A,{code:`for CTX in [[GLOBAL:FIRST_REGION_CONTEXT]] [[GLOBAL:SECOND_REGION_CONTEXT]]; do
76echo "=== $CTX ==="
77kubectl --context "$CTX" get pods -n vcluster-platform -l app=loft
78echo
79done`,language:"bash"}),"\n",(0,s.jsx)(t.p,{children:"Verify the platform UI is accessible through the shared DNS domain and that both\nRoute 53 health checks return healthy."})]})}function b(e={}){let{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(f,{...e})}):f(e)}},16355(e,t,r){r.d(t,{A:()=>u});var a=r(74848),s=r(96540),o=r(16151),n=r.n(o),i=r(56347),l=r(80698),d=r(23652),c=JSON.parse('{"iD":"4.11.3","FQ":"0.36.2"}');let p=(e,t)=>`var-${e}-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}`,u=({code:e="",language:t="bash",title:r})=>{let o,u=(0,s.useId)(),h=(0,d.kb)(),m=(0,i.zy)(),f=c.iD,b=c.FQ;try{if(m.pathname.includes("/platform/")){let e=(0,l.zK)("platform");e?.activeVersion?.name&&"current"!==e.activeVersion.name&&(f=e.activeVersion.name)}}catch(e){}try{if(m.pathname.includes("/vcluster/")){let e=(0,l.zK)("vcluster");e?.activeVersion?.name&&"current"!==e.activeVersion.name&&(b=e.activeVersion.name)}}catch(e){}let g="string"==typeof e?e.replace(/__PLATFORM_VERSION__/g,f).replace(/__PLATFORM_VERSION_MINOR__/g,f.split(".").slice(0,2).join(".")).replace(/__VCLUSTER_VERSION__/g,b).replace(/__VCLUSTER_VERSION_MINOR__/g,b.split(".").slice(0,2).join(".")):e,v=/\[\[VAR:([^:]+):([^\]]*)\]\]/g,x={},_=new RegExp(v);if("string"==typeof g)for(;null!==(o=_.exec(g));)x[o[1]]=o[2];
79let[A,O]=(0,s.useState)(x),S="string"==typeof g?g.replace(/\[\[GLOBAL:([^\]]+)\]\]/g,(e,t)=>h[t]||`[[GLOBAL:${t}]]`).replace(v,(e,t)=>A[t]||x[t]||""):g;return 0===Object.keys(x).length?(0,a.jsx)(n(),{language:t,title:r,children:S}):(0,a.jsxs)("div",{className:"interpolated-code-wrapper",style:{marginBottom:"var(--ifm-leading)",border:"1px solid var(--ifm-color-emphasis-300)",borderRadius:"var(--ifm-code-border-radius)",backgroundColor:"rgba(0, 0, 0, 0.02)",padding:"1rem"},children:[(0,a.jsxs)("div",{className:"interpolated-code-inputs",style:{marginBottom:"1rem"},children:[(0,a.jsx)("div",{style:{fontSize:"0.8rem",color:"var(--ifm-color-emphasis-600)",marginBottom:"8px",fontStyle:"italic"},children:"Modify the following with your specific values to generate a copyable command:"}),Object.entries(x).map(([e,t])=>(0,a.jsxs)("div",{style:{display:"flex",alignItems:"center",marginBottom:"0.5rem",fontFamily:"var(--ifm-font-family-monospace)"},children:[(0,a.jsx)("label",{htmlFor:p(u,e),style:{minWidth:"150px",color:"var(--ifm-font-color-base)",fontSize:"0.9rem",fontWeight:"600",marginRight:"10px"},children:e}),(0,a.jsx)("input",{id:p(u,e),type:"text",value:A[e]||"",onChange:t=>O(r=>({...r,[e]:t.target.value})),placeholder:t,style:{backgroundColor:"rgba(255, 255, 255, 0.7)",border:"1px solid var(--ifm-color-emphasis-300)",borderRadius:"var(--ifm-global-radius)",padding:"0.3rem 0.5rem",color:"var(--ifm-font-color-base)",fontFamily:"var(--ifm-font-family-monospace)",fontSize:"0.9rem",width:"100%",outline:"none",transition:"all 0.2s ease"},onFocus:e=>{e.target.style.borderColor="var(--ifm-color-primary)",e.target.style.boxShadow="0 0 0 1px var(--ifm-color-primary)"},onBlur:e=>{e.target.style.borderColor="var(--ifm-color-emphasis-300)",e.target.style.boxShadow="none"}})]},e))]}),(0,a.jsx)("div",{children:(0,a.jsx)(n(),{language:t,title:r,children:S})})]})}},23652(e,t,r){r.d(t,{Lg:()=>l,di:()=>i,kb:()=>d});var a=r(96540);let s={},o=[],n=()=>{o.forEach(e=>e(s))},i=e=>{s={...s,...e},n()},l=(e,t)=>{s={...s,[e]:t},n()},d=()=>{let[e,t]=(0,a.useState)({...s});return(0,a.useEffect)(()=>{var e;return e=e=>{t({...e})},o.push(e),()=>{o=o.filter(t=>t!==e)}},[]),e}},82187(e,t,r){r.d(t,{A:()=>i});var a=r(74848),s=r(96540),o=r(23652);let n=(e,t)=>`var-${e}-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}`,i=e=>{let t=(0,s.useId)(),r=s.useRef(!1);r.current||((0,o.di)(e),r.current=!0);let i=(0,o.kb)();return((0,s.useEffect)(()=>()=>{},[]),0===Object.keys(e).length)?null:(0,a.jsxs)("div",{className:"page-variables-wrapper",style:{marginBottom:"var(--ifm-leading)",border:"1px solid var(--ifm-color-emphasis-300)",borderRadius:"var(--ifm-code-border-radius)",backgroundColor:"rgba(0, 0, 0, 0.02)",padding:"1rem"},children:[(0,a.jsx)("div",{style:{fontSize:"0.8rem",color:"var(--ifm-color-emphasis-600)",marginBottom:"8px",fontStyle:"italic"},children:"Modify the following with your specific values to replace on the whole page and generate copyable commands:"}),Object.entries(e).map(([e,r])=>(0,a.jsxs)("div",{style:{display:"flex",alignItems:"center",marginBottom:"0.5rem",fontFamily:"var(--ifm-font-family-monospace)"},children:[(0,a.jsx)("label",{htmlFor:n(t,e),style:{minWidth:"200px",color:"var(--ifm-font-color-base)",fontSize:"0.9rem",fontWeight:"600",marginRight:"10px"},children:e}),(0,a.jsx)("input",{id:n(t,e),type:"text",value:i[e]||r,onChange:t=>(0,o.Lg)(e,t.target.value),placeholder:r,style:{backgroundColor:"rgba(255, 255, 255, 0.7)",border:"1px solid var(--ifm-color-emphasis-300)",borderRadius:"var(--ifm-global-radius)",padding:"0.3rem 0.5rem",color:"var(--ifm-font-color-base)",fontFamily:"var(--ifm-font-family-monospace)",fontSize:"0.9rem",width:"100%",outline:"none",transition:"all 0.2s ease"},onFocus:e=>{e.target.style.borderColor="var(--ifm-color-primary)",e.target.style.boxShadow="0 0 0 1px var(--ifm-color-primary)"},onBlur:e=>{e.target.style.borderColor="var(--ifm-color-emphasis-300)",e.target.style.boxShadow="none"}})]},e))]})}},21472(e,t,r){r.d(t,{A:()=>i});var a=r(74848);r(96540);var s=r(77444);let o="versionBadge_S5ga",n=({platformVersion:e,vclusterVersion:t})=>e||t?(0,a.jsxs)(s.A,{type:"info",children:[e&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("span",{children:["This feature is available from the ",(0,a.jsx)("strong",{children:"Platform"})," version "]}),(0,a.jsx)("span",{className:o,children:e})]}),e&&t&&(0,a.jsx)("span",{children:" and "}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("span",{children:[!e&&"This feature ","
79was introduced in ",(0,a.jsx)("strong",{children:"vCluster"})," version"," "]}),(0,a.jsx)("span",{className:o,children:t})]})]}):(console.error("VersionBadge: Either platformVersion or vclusterVersion must be provided"),null);n.propTypes={platformVersion:function(e,t,r){if(!e.platformVersion&&!e.vclusterVersion)return Error(`Either 'platformVersion' or 'vclusterVersion' must be provided in '${r}'`)},vclusterVersion:function(e,t,r){if(!e.platformVersion&&!e.vclusterVersion)return Error(`Either 'platformVersion' or 'vclusterVersion' must be provided in '${r}'`)}};let i=n}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.