[{"data":1,"prerenderedAt":1254},["ShallowReactive",2],{"page:\u002Fmigrate":3},{"id":4,"title":5,"accent":6,"body":7,"coming":1242,"description":1243,"extension":1244,"headline":1245,"intro":1246,"meta":1247,"navigation":365,"path":1248,"repos":1249,"seo":1250,"stats":1251,"statsDetail":6,"statsNote":6,"stem":1252,"__hash__":1253},"content\u002Fmigrate.md","Migrate",null,{"type":8,"value":9,"toc":1225},"minimark",[10,15,23,26,101,104,113,167,173,182,189,195,200,209,228,231,313,331,336,342,449,460,689,693,704,860,864,874,880,889,902,908,924,927,931,944,953,970,974,993,1008,1014,1023,1027,1042,1051,1058,1078,1085,1089,1103,1128,1132,1147,1151,1171,1175,1193,1208,1212,1218,1221],[11,12,14],"h2",{"id":13},"before-you-start","🧰 Before You Start",[16,17,18,22],"p",{},[19,20,21],"strong",{},"Everything runs on your own computer."," drangler is installed on your laptop or workstation, not on\nthe server. It connects to the server over SSH, runs a short list of read-only commands there, and\ncopies the results back.",[16,24,25],{},"You need three things:",[27,28,29,53,83],"ol",{},[30,31,32,35,36,40,41,44,45,48,49,52],"li",{},[19,33,34],{},"drangler",", with Node 24 or newer: ",[37,38,39],"code",{},"npm i -g @drupflare\u002Fdrangler",". It uses ",[37,42,43],{},"ssh"," and\n",[37,46,47],{},"wrangler",", which ",[37,50,51],{},"npm i -g wrangler"," installs.",[30,54,55,58,59,62,63,79,82],{},[19,56,57],{},"SSH access to the server"," as a user who can run ",[37,60,61],{},"drush"," in the Drupal root. This is the test:",[64,65,70],"pre",{"className":66,"code":67,"language":68,"meta":69,"style":69},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ssh deploy@old.example 'cd \u002Fvar\u002Fwww\u002Fhtml && drush status'\n","sh","",[37,71,72],{"__ignoreMap":69},[73,74,77],"span",{"class":75,"line":76},"line",1,[73,78,67],{},[80,81],"br",{},"If it prints the Drupal version, drangler can read the site.",[30,84,85,88,89,92,93,96,97,100],{},[19,86,87],{},"A Cloudflare account",", signed in through wrangler. ",[37,90,91],{},"wrangler login"," opens a browser to approve\naccess, and ",[37,94,95],{},"drangler cf whoami"," confirms which account drangler will use. On a machine with no\nbrowser, such as CI, set ",[37,98,99],{},"CLOUDFLARE_API_TOKEN"," instead.",[16,102,103],{},"Then check everything at once. This checks your machine, then runs the read-only survey against the\nserver and names anything missing:",[64,105,107],{"className":66,"code":106,"language":68,"meta":69,"style":69},"drangler doctor --source deploy@old.example --root \u002Fvar\u002Fwww\u002Fhtml\n",[37,108,109],{"__ignoreMap":69},[73,110,111],{"class":75,"line":76},[73,112,106],{},[16,114,115,118,119,122,123,126,127,130,131,133,134,137,138,140,141,143,144,146,147,150,151,154,155,158,159,162,163,166],{},[19,116,117],{},"Finding the Drupal root."," You do not have to. Without ",[37,120,121],{},"--root",", ",[37,124,125],{},"drangler preview"," reads the\nserver's Apache or nginx configuration, picks the document root that holds Drupal's ",[37,128,129],{},"index.php",", and\nfinds the Composer project above it, the project's own ",[37,132,61],{},", the web server's user and the\n",[37,135,136],{},"sites\u002F"," directory, so a site that lives somewhere unusual is found from what is on the server. If\nyou want to name it, ",[37,139,121],{}," is the directory with ",[37,142,129],{}," and ",[37,145,136],{}," in it, usually\n",[37,148,149],{},"\u002Fvar\u002Fwww\u002Fhtml"," or ",[37,152,153],{},"\u002Fvar\u002Fwww\u002F\u003Csite>\u002Fweb",". ",[37,156,157],{},"drush status"," prints it as ",[37,160,161],{},"Drupal root",". When one server\nruns several Drupal sites, ",[37,164,165],{},"--site \u003Chostname>"," says which one.",[16,168,169,172],{},[19,170,171],{},"If your code is in git."," A preview copies the code on the server's disk, including edits nobody\ncommitted. To see whether the server matches your repository first:",[64,174,176],{"className":66,"code":175,"language":68,"meta":69,"style":69},"ssh deploy@old.example 'cd \u002Fvar\u002Fwww\u002Fhtml && git status --short && git log -1 --oneline'\n",[37,177,178],{"__ignoreMap":69},[73,179,180],{"class":75,"line":76},[73,181,175],{},[16,183,184,185,188],{},"An empty ",[37,186,187],{},"git status"," means the server holds exactly the commit printed below it.",[11,190,192,193],{"id":191},"one-command-drangler-preview","⚡ One Command: ",[37,194,125],{},[16,196,197,199],{},[37,198,125],{}," does the whole move for you. It surveys the current server, explains what will\nchange, converts the database, copies the files, and brings up a working duplicate of the site,\neither on your own machine or on your Cloudflare account, so you can click through it before\ndeciding anything.",[64,201,203],{"className":66,"code":202,"language":68,"meta":69,"style":69},"drangler preview --host deploy@old.example\n",[37,204,205],{"__ignoreMap":69},[73,206,207],{"class":75,"line":76},[73,208,202],{},[16,210,211,215,216,219,220,223,224,227],{},[212,213,214],"pitch",{},"A preview is a duplicate, never a replacement."," Every command it runs on the old server is\nread-only, the database and files are streamed back, and the live site keeps serving the whole time.\nIt asks before each step. ",[37,217,218],{},"--full"," runs every step after a single confirmation, ",[37,221,222],{},"--confirm \u003Chost>","\nruns every step with no prompt, and ",[37,225,226],{},"--dry-run"," shows every step and command without running any of\nthem.",[16,229,230],{},"What the duplicate carries:",[232,233,234,240,246,265,279,289,299],"ul",{},[30,235,236,239],{},[19,237,238],{},"The database",", from MySQL, MariaDB or PostgreSQL, with the administrator accounts and passwords\nit already had.",[30,241,242,245],{},[19,243,244],{},"Uploaded files",", public and private. Image styles and aggregated CSS and JS are rebuilt instead\nof copied.",[30,247,248,251,252,122,255,122,258,143,261,264],{},[19,249,250],{},"Custom and contrib code"," in ",[37,253,254],{},"modules\u002F",[37,256,257],{},"themes\u002F",[37,259,260],{},"profiles\u002F",[37,262,263],{},"libraries\u002F",". Composer\nlibraries the duplicate lacks are installed from the registry after the claim; any that fail are\nlisted as ready commands.",[30,266,267,270,271,274,275,278],{},[19,268,269],{},"Settings",": literal ",[37,272,273],{},"$config"," overrides, module settings written as literal ",[37,276,277],{},"$settings"," values,\nand a Redis connection as a Redis URL.",[30,280,281,284,285,288],{},[19,282,283],{},"Application state kept in Redis",", such as mailed tokens and markers that stop a notification\nrepeating. A database dump cannot hold it, so preview reads the keys with one fixed read-only\nscript and stores them in the duplicate's own database, where a ",[37,286,287],{},"\\Redis"," stand-in serves them.",[30,290,291,294,295,298],{},[19,292,293],{},"Server rules",": unconditional redirects and response headers from ",[37,296,297],{},".htaccess",". Anything more\ninvolved is listed for you to review.",[30,300,301,304,305,308,309,312],{},[19,302,303],{},"Docroot files"," a browser fetches by name, such as search-engine verification pages, ",[37,306,307],{},"ads.txt","\nand ",[37,310,311],{},".well-known\u002F",".",[16,314,315,316,319,320,323,324,326,327,330],{},"It ends by checking the duplicate against the source: content and file counts, then the front page,\nthe login page, a node, a public file and any page you add with ",[37,317,318],{},"--check",". Given the live site's\naddress with ",[37,321,322],{},"--source-url",", each page must answer with the same status there, and a JSON page must\nreturn the same keys. A page the source answers from your own Cloudflare Access is reported as not\ncompared, because the edge answered there and Drupal did not.\n",[37,325,125],{}," needs drangler 0.4 or later and Drupflare 1.0.4 or later, which sends the request\nheaders a headless site reads. Discovery, credential handling, ",[37,328,329],{},"--follow"," and deploys behind Cloudflare\nAccess arrived in drangler 0.4; the command itself has been there since 0.3.",[332,333,335],"h3",{"id":334},"reading-a-dry-run","Reading a Dry Run",[16,337,338,339,341],{},"Add ",[37,340,226],{}," to see the whole plan before anything happens. Nothing is executed and nothing is\ncopied:",[64,343,347],{"className":344,"code":345,"language":346,"meta":69,"style":69},"language-console shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","$ drangler preview --host deploy@old.example --dry-run\ndry run against deploy@old.example; nothing was executed\n\n1. Find the Site on the Host\n2. Survey the Source\n...\n12. Check the Duplicate\n\nread-only commands on the host\n  $ php -v\n  $ cd \u002Fvar\u002Fwww\u002Fhtml && drush status --format=json\n  $ tar -cf - -C \u002Fvar\u002Fwww\u002Fhtml\u002Fsites\u002Fdefault files\n  ...\n\nlocal commands\n  $ drangler migrate install --db .drangler\u002Fpreview\u002Fold.example\u002Fsite.sqlite --repack ...\n  $ bunx wrangler dev --port 8787\n","console",[37,348,349,354,360,367,373,379,385,391,396,402,408,414,420,426,431,437,443],{"__ignoreMap":69},[73,350,351],{"class":75,"line":76},[73,352,353],{},"$ drangler preview --host deploy@old.example --dry-run\n",[73,355,357],{"class":75,"line":356},2,[73,358,359],{},"dry run against deploy@old.example; nothing was executed\n",[73,361,363],{"class":75,"line":362},3,[73,364,366],{"emptyLinePlaceholder":365},true,"\n",[73,368,370],{"class":75,"line":369},4,[73,371,372],{},"1. Find the Site on the Host\n",[73,374,376],{"class":75,"line":375},5,[73,377,378],{},"2. Survey the Source\n",[73,380,382],{"class":75,"line":381},6,[73,383,384],{},"...\n",[73,386,388],{"class":75,"line":387},7,[73,389,390],{},"12. Check the Duplicate\n",[73,392,394],{"class":75,"line":393},8,[73,395,366],{"emptyLinePlaceholder":365},[73,397,399],{"class":75,"line":398},9,[73,400,401],{},"read-only commands on the host\n",[73,403,405],{"class":75,"line":404},10,[73,406,407],{},"  $ php -v\n",[73,409,411],{"class":75,"line":410},11,[73,412,413],{},"  $ cd \u002Fvar\u002Fwww\u002Fhtml && drush status --format=json\n",[73,415,417],{"class":75,"line":416},12,[73,418,419],{},"  $ tar -cf - -C \u002Fvar\u002Fwww\u002Fhtml\u002Fsites\u002Fdefault files\n",[73,421,423],{"class":75,"line":422},13,[73,424,425],{},"  ...\n",[73,427,429],{"class":75,"line":428},14,[73,430,366],{"emptyLinePlaceholder":365},[73,432,434],{"class":75,"line":433},15,[73,435,436],{},"local commands\n",[73,438,440],{"class":75,"line":439},16,[73,441,442],{},"  $ drangler migrate install --db .drangler\u002Fpreview\u002Fold.example\u002Fsite.sqlite --repack ...\n",[73,444,446],{"class":75,"line":445},17,[73,447,448],{},"  $ bunx wrangler dev --port 8787\n",[16,450,451,452,455,456,459],{},"The numbered list is the twelve steps it will ask about. ",[19,453,454],{},"Read-only commands on the host"," run on\nthe server over SSH, and every one passes an allow-list first, so a command that could write is\nrefused before it leaves your computer. ",[19,457,458],{},"Local commands"," run on your computer.",[461,462,463,476],"table",{},[464,465,466],"thead",{},[467,468,469,473],"tr",{},[470,471,472],"th",{},"Flag",[470,474,475],{},"What it does",[477,478,479,500,509,519,529,538,547,556,566,579,589,599,609,623,633,643,656,665,679],"tbody",{},[467,480,481,487],{},[482,483,484],"td",{},[37,485,486],{},"--host",[482,488,489,490,492,493,496,497],{},"the server, typed the way you would for ",[37,491,43],{},": ",[37,494,495],{},"user@host",", or ",[37,498,499],{},"user@host:port",[467,501,502,506],{},[482,503,504],{},[37,505,121],{},[482,507,508],{},"the Drupal root on that server, when you would rather not have it found",[467,510,511,516],{},[482,512,513],{},[37,514,515],{},"--site",[482,517,518],{},"which site to take when the server runs several",[467,520,521,526],{},[482,522,523],{},[37,524,525],{},"--identity",[482,527,528],{},"an SSH key file, when it is not your default key",[467,530,531,535],{},[482,532,533],{},[37,534,226],{},[482,536,537],{},"list every step and command, and run none of them",[467,539,540,544],{},[482,541,542],{},[37,543,218],{},[482,545,546],{},"run every step after one confirmation, instead of asking before each",[467,548,549,553],{},[482,550,551],{},[37,552,222],{},[482,554,555],{},"run every step with no prompt; the value must name the host, as typing it would",[467,557,558,563],{},[482,559,560],{},[37,561,562],{},"--check \u003Cpath...>",[482,564,565],{},"pages to compare with the live site, beside the front page and the login page",[467,567,568,573],{},[482,569,570],{},[37,571,572],{},"--follow \u003Cmodule=repo...>",[482,574,575,576],{},"keep a module current from a git repository, as ",[37,577,578],{},"mymodule=my-org\u002Fmymodule",[467,580,581,586],{},[482,582,583],{},[37,584,585],{},"--follow-interval",[482,587,588],{},"minutes between polls of a followed repository; 5 by default, and the least",[467,590,591,596],{},[482,592,593],{},[37,594,595],{},"--carry-key \u003Cname...>",[482,597,598],{},"bring a blanked credential into the duplicate",[467,600,601,606],{},[482,602,603],{},[37,604,605],{},"--hold-mail",[482,607,608],{},"on your own machine, queue the duplicate's mail and never send it",[467,610,611,616],{},[482,612,613],{},[37,614,615],{},"--edge [branch]",[482,617,618,619,622],{},"use a prerelease worker: the tip of a branch (",[37,620,621],{},"master"," when bare), from the CDN",[467,624,625,630],{},[482,626,627],{},[37,628,629],{},"--plan \u003Cfree or paid>",[482,631,632],{},"the Workers plan the duplicate models",[467,634,635,640],{},[482,636,637],{},[37,638,639],{},"--deploy",[482,641,642],{},"put the duplicate on your Cloudflare account, behind Access, not your computer",[467,644,645,650],{},[482,646,647],{},[37,648,649],{},"--access-email",[482,651,652,653,655],{},"with ",[37,654,639],{},", who may open the copy; the wrangler login by default",[467,657,658,662],{},[482,659,660],{},[37,661,322],{},[482,663,664],{},"the live site's address, so each checked page must match its status there",[467,666,667,672],{},[482,668,669],{},[37,670,671],{},"--out",[482,673,674,675,678],{},"where the copy is kept; ",[37,676,677],{},".drangler\u002Fpreview\u002F\u003Chost>"," by default",[467,680,681,686],{},[482,682,683],{},[37,684,685],{},"--port",[482,687,688],{},"the local port for the duplicate; 8787 by default",[332,690,692],{"id":691},"answering-without-a-prompt","Answering Without a Prompt",[16,694,695,696,699,700,703],{},"Every choice has a flag, a ",[37,697,698],{},"DRANGLER_PREVIEW_\u003CNAME>"," variable and a prompt on a terminal. A flag wins\nover a variable, a list is comma separated, a switch is ",[37,701,702],{},"1",", and a question nobody can answer is\nrefused instead of left waiting, so a CI job never hangs.",[461,705,706,716],{},[464,707,708],{},[467,709,710,713],{},[470,711,712],{},"Variable",[470,714,715],{},"Same as",[477,717,718,729,740,752,764,775,786,797,809,820,835,848],{},[467,719,720,725],{},[482,721,722],{},[37,723,724],{},"DRANGLER_PREVIEW_ROOT",[482,726,727],{},[37,728,121],{},[467,730,731,736],{},[482,732,733],{},[37,734,735],{},"DRANGLER_PREVIEW_SITE",[482,737,738],{},[37,739,515],{},[467,741,742,747],{},[482,743,744],{},[37,745,746],{},"DRANGLER_PREVIEW_CONFIRM",[482,748,749],{},[37,750,751],{},"--confirm",[467,753,754,759],{},[482,755,756],{},[37,757,758],{},"DRANGLER_PREVIEW_PLAN",[482,760,761],{},[37,762,763],{},"--plan",[467,765,766,771],{},[482,767,768],{},[37,769,770],{},"DRANGLER_PREVIEW_CHECK",[482,772,773],{},[37,774,318],{},[467,776,777,782],{},[482,778,779],{},[37,780,781],{},"DRANGLER_PREVIEW_FOLLOW",[482,783,784],{},[37,785,329],{},[467,787,788,793],{},[482,789,790],{},[37,791,792],{},"DRANGLER_PREVIEW_FOLLOW_INTERVAL",[482,794,795],{},[37,796,585],{},[467,798,799,804],{},[482,800,801],{},[37,802,803],{},"DRANGLER_PREVIEW_CARRY_KEYS",[482,805,806],{},[37,807,808],{},"--carry-key",[467,810,811,816],{},[482,812,813],{},[37,814,815],{},"DRANGLER_PREVIEW_ACCESS_EMAIL",[482,817,818],{},[37,819,649],{},[467,821,822,827],{},[482,823,824],{},[37,825,826],{},"DRANGLER_PREVIEW_DEPLOY",[482,828,829,831,832,834],{},[37,830,639],{}," (",[37,833,702],{},")",[467,836,837,842],{},[482,838,839],{},[37,840,841],{},"DRANGLER_PREVIEW_HOLD_MAIL",[482,843,844,831,846,834],{},[37,845,605],{},[37,847,702],{},[467,849,850,855],{},[482,851,852],{},[37,853,854],{},"DRANGLER_PREVIEW_EDGE",[482,856,857],{},[37,858,859],{},"--edge",[332,861,863],{"id":862},"what-preview-decides-for-you","What Preview Decides for You",[16,865,866,869,870,873],{},[212,867,868],{},"A copy of your site should not be able to act as your site."," Sites often keep secrets in\nDrupal's own configuration: payment keys, the key to a backend API, a mail login, OAuth client\nsecrets. They travel in the database, and a copy that holds them can charge cards, write to your\nAPI or push to your users' phones. Preview finds every one of them, empties them in the duplicate\nwhile the database stays as it was, and lists them by name. Reads that need no key still work and\nanything that needs one fails closed. ",[37,871,872],{},"--carry-key mantle2_api_key"," brings one back on purpose.",[16,875,876,879],{},[19,877,878],{},"Stylesheets and scripts."," Drupal's own CSS and JS aggregation cannot run on a Worker, and a site\nthat had it on loses its theme without it. Preview turns it off in the duplicate and tells you when\nyour site had it on. The worker combines files at the edge instead.",[16,881,882,885,886,888],{},[19,883,884],{},"Mail."," A mail setup that opens its own connection to a mail server, such as the SMTP module under\nMail System, cannot run on a Worker, so preview moves it onto the worker's mail layer. With the logins\nblank that layer has no relay and the copy sends nothing until you give it one. On your own machine\n",[37,887,605],{}," queues each message instead, so you can read a verification code from the queue.",[16,890,891,894,895,897,898,901],{},[19,892,893],{},"Cloudflare Access."," ",[37,896,639],{}," first creates an Access application on the ",[37,899,900],{},"workers.dev"," address,\nwith a policy for one email address and a service token for drangler, and waits until the edge turns\nstrangers away. Only then does it upload. Nothing in DNS changes. The API token needs Access edit\nrights as well as Workers; without them preview stops before it creates or uploads anything and\nsays which permission is missing.",[16,903,904,907],{},[19,905,906],{},"A slow first start."," A new site unpacks its whole database the first time it starts, and on a large\none the local runtime can end during it. Preview starts it again if it ends, up to five starts in all, and counts the\nsite up after two good answers in a row.",[16,909,910,894,913,916,917,920,921,312],{},[19,911,912],{},"Your own modules.",[37,914,915],{},"--follow mymodule=my-org\u002Fmymodule"," adds the repository once the copy is claimed\nand the site polls it every five minutes, so a push to the repository reaches the copy. A public\nrepository needs no token. The repository's ",[37,918,919],{},"vendor\u002F"," folder is never delivered, so a change that adds\na Composer package needs the module's own CI to run ",[37,922,923],{},"drangler build --project",[16,925,926],{},"The steps below are what it runs, and each is also a command of its own.",[11,928,930],{"id":929},"_1-survey-without-touching-anything","🔎 1. Survey, Without Touching Anything",[16,932,933,936,937,940,941,943],{},[37,934,935],{},"drangler migrate survey"," connects to the current server over SSH and runs a short list of read-only\ncommands: PHP and Drupal versions, the database, enabled modules, files, and content counts. ",[212,938,939],{},"Nothing is written."," Add ",[37,942,226],{}," to see every command before you hand over a key.",[64,945,947],{"className":66,"code":946,"language":68,"meta":69,"style":69},"drangler migrate survey --host deploy@old.example --root \u002Fvar\u002Fwww\u002Fhtml --out survey.json\n",[37,948,949],{"__ignoreMap":69},[73,950,951],{"class":75,"line":76},[73,952,946],{},[16,954,955,957,958,961,962,965,966,969],{},[37,956,671],{}," saves the result as a file the next steps read. If the connection drops part-way,\n",[37,959,960],{},"--resume"," re-runs only the steps that did not finish. In a dry run, a step marked ",[37,963,964],{},"(optional)"," is\none whose failure does not stop the survey: a missing ",[37,967,968],{},"file_managed"," count, for example, is recorded\nand the survey carries on.",[11,971,973],{"id":972},"_2-get-a-verdict-before-you-commit","🚦 2. Get a Verdict Before You Commit",[16,975,976,979,980,122,983,496,986,989,990,312],{},[37,977,978],{},"drangler migrate eligibility"," turns the survey into one of three answers: ",[19,981,982],{},"GO",[19,984,985],{},"GO WITH\nCHANGES",[19,987,988],{},"NO",", and it names the reason behind every finding. Anything it could not measure is\nreported as unmeasured, ",[212,991,992],{},"never quietly passed",[64,994,996],{"className":66,"code":995,"language":68,"meta":69,"style":69},"drangler migrate eligibility --survey survey.json\ndrangler migrate plan --survey survey.json --to workers\n",[37,997,998,1003],{"__ignoreMap":69},[73,999,1000],{"class":75,"line":76},[73,1001,1002],{},"drangler migrate eligibility --survey survey.json\n",[73,1004,1005],{"class":75,"line":356},[73,1006,1007],{},"drangler migrate plan --survey survey.json --to workers\n",[16,1009,1010,1013],{},[37,1011,1012],{},"migrate plan"," orders the work: which modules move as they are, which need a service provisioned,\nwhich cannot run on Workers and what replaces them.",[16,1015,1016,1017,154,1020],{},"A Drupal 10 site is planned as a major upgrade on the way in. The plan names every enabled contrib\nmodule that has no Drupal 11 release, and the database updates run after the move with\n",[37,1018,1019],{},"drangler site updb",[212,1021,1022],{},"Drupal 10 reaches end of life on December 9, 2026.",[11,1024,1026],{"id":1025},"_3-convert-the-database","🔁 3. Convert the Database",[16,1028,1029,1030,1033,1034,1037,1038,1041],{},"A MySQL, MariaDB or PostgreSQL dump converts to the SQLite a Drupflare site runs on (",[37,1031,1032],{},"--from pgsql","\nreads a ",[37,1035,1036],{},"pg_dump","). The dump comes from the\nserver and the rest runs on your computer, which needs ",[37,1039,1040],{},"sqlite3"," installed:",[64,1043,1045],{"className":66,"code":1044,"language":68,"meta":69,"style":69},"ssh deploy@old.example 'cd \u002Fvar\u002Fwww\u002Fhtml && drush sql:dump --extra-dump=--hex-blob' > site.sql\n",[37,1046,1047],{"__ignoreMap":69},[73,1048,1049],{"class":75,"line":76},[73,1050,1044],{},[16,1052,1053,1054,1057],{},"The converter ",[212,1055,1056],{},"refuses what it cannot translate exactly"," and names the statement, instead of producing a dump that looks complete\nand fails later.",[64,1059,1061],{"className":66,"code":1060,"language":68,"meta":69,"style":69},"drangler migrate convert --from mysql --to sqlite --in site.sql --out site.sqlite.sql\nsqlite3 site.sqlite \u003C site.sqlite.sql\ndrangler migrate install --db site.sqlite --repack\n",[37,1062,1063,1068,1073],{"__ignoreMap":69},[73,1064,1065],{"class":75,"line":76},[73,1066,1067],{},"drangler migrate convert --from mysql --to sqlite --in site.sql --out site.sqlite.sql\n",[73,1069,1070],{"class":75,"line":356},[73,1071,1072],{},"sqlite3 site.sqlite \u003C site.sqlite.sql\n",[73,1074,1075],{"class":75,"line":362},[73,1076,1077],{},"drangler migrate install --db site.sqlite --repack\n",[16,1079,1080,1081,1084],{},"Every file it replaces is backed up and verified first, and ",[37,1082,1083],{},"drangler migrate restore"," puts a backup\nset back with one command.",[11,1086,1088],{"id":1087},"_4-bring-the-code","📦 4. Bring the Code",[16,1090,1091,1092,1095,1096,1098,1099,1102],{},"The database names the modules a site runs, and the code arrives separately, in two parts. The\ncontrib packages in the project's ",[37,1093,1094],{},"composer.lock"," go up with ",[37,1097,923],{},", which runs\ncomposer on your machine, applies the project's patches, including patched Drupal core, reaches its\nprivate repositories, and uploads only what the shipped pack does not already carry. The install\nprofile, custom modules and themes go up with ",[37,1100,1101],{},"drangler modify upload",", which boots Drupal on the\nsite before a revision goes live and rolls back one that would fatal.",[64,1104,1106],{"className":66,"code":1105,"language":68,"meta":69,"style":69},"drangler site claim https:\u002F\u002Fmy-site.example --title \"My Site\"\ndrangler build --project ~\u002Fwork\u002Fmy-site --site https:\u002F\u002Fmy-site.example --yes\ndrangler modify upload --dir ~\u002Fwork\u002Fmy-site\u002Fweb\u002Fprofiles\u002Fcustom\u002Fmy_profile --site https:\u002F\u002Fmy-site.example --yes\ndrangler site updb https:\u002F\u002Fmy-site.example --steps 200 --no-snapshot --yes\n",[37,1107,1108,1113,1118,1123],{"__ignoreMap":69},[73,1109,1110],{"class":75,"line":76},[73,1111,1112],{},"drangler site claim https:\u002F\u002Fmy-site.example --title \"My Site\"\n",[73,1114,1115],{"class":75,"line":356},[73,1116,1117],{},"drangler build --project ~\u002Fwork\u002Fmy-site --site https:\u002F\u002Fmy-site.example --yes\n",[73,1119,1120],{"class":75,"line":362},[73,1121,1122],{},"drangler modify upload --dir ~\u002Fwork\u002Fmy-site\u002Fweb\u002Fprofiles\u002Fcustom\u002Fmy_profile --site https:\u002F\u002Fmy-site.example --yes\n",[73,1124,1125],{"class":75,"line":369},[73,1126,1127],{},"drangler site updb https:\u002F\u002Fmy-site.example --steps 200 --no-snapshot --yes\n",[11,1129,1131],{"id":1130},"_5-deploy-and-cut-over","🚀 5. Deploy and Cut Over",[16,1133,1134,1135,1138,1139,1142,1143,1146],{},"Deploy to your Cloudflare account, then switch traffic with ",[212,1136,1137],{},"a short read-only window",".\n",[37,1140,1141],{},"drangler migrate cutover"," prints the checklist a person confirms, and ",[37,1144,1145],{},"drangler migrate delta","\ncarries the content that changed during the move, and prints the statement that re-seeds the id\ncounters so the first new node on the new site does not collide with an old one.",[11,1148,1150],{"id":1149},"check-it-yourself","🧪 Check It Yourself",[16,1152,1153,1160,1161,1165,1166,312],{},[1154,1155,1159],"a",{"href":1156,"rel":1157},"https:\u002F\u002Fgithub.com\u002Fthunder\u002Fthunder-distribution",[1158],"nofollow","Thunder"," is one of the Verified codebases on the\n",[1154,1162,1164],{"href":1163},"\u002Ffixtures","fixtures page",". You can rebuild that result on your own machine, from an empty checkout\nto a scored site, with the steps in\n",[1154,1167,1170],{"href":1168,"rel":1169},"https:\u002F\u002Fdocs.drupflare.com\u002Fgetting-started\u002Freproduce-thunder.html",[1158],"Reproducing Thunder",[11,1172,1174],{"id":1173},"leaving-is-part-of-the-plan","🚪 Leaving Is Part of the Plan",[16,1176,1177,1178,1181,1182,1185,1186,1189,1190],{},"A site can move back to an ordinary server at any time. ",[37,1179,1180],{},"drangler migrate export"," pulls the database\nout, ",[37,1183,1184],{},"migrate convert"," turns it back into MySQL, and ",[37,1187,1188],{},"migrate eligibility --to vps"," lists the few\nsettings a conventional host needs instead. Uploaded files are copied separately. ",[212,1191,1192],{},"No lock-in is a promise you can test on day one.",[64,1194,1196],{"className":66,"code":1195,"language":68,"meta":69,"style":69},"drangler migrate export --url my-site.example --out worker.sql\ndrangler migrate convert --from sqlite --to mysql --in worker.sql --out vps.sql\n",[37,1197,1198,1203],{"__ignoreMap":69},[73,1199,1200],{"class":75,"line":76},[73,1201,1202],{},"drangler migrate export --url my-site.example --out worker.sql\n",[73,1204,1205],{"class":75,"line":356},[73,1206,1207],{},"drangler migrate convert --from sqlite --to mysql --in worker.sql --out vps.sql\n",[11,1209,1211],{"id":1210},"️-built-for-organisations-that-cannot-rebuild","🏛️ Built for Organisations That Cannot Rebuild",[16,1213,1214,1215,312],{},"Universities, councils and non-profits often run dozens of small Drupal sites that were built years\nago and still matter. Rewriting them is not on anyone's roadmap. Drupflare keeps them as they are,\nremoves the servers underneath, and ",[212,1216,1217],{},"makes every step reversible",[1219,1220],"deploy-button",{},[1222,1223,1224],"style",{},"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":69,"searchDepth":356,"depth":356,"links":1226},[1227,1228,1234,1235,1236,1237,1238,1239,1240,1241],{"id":13,"depth":356,"text":14},{"id":191,"depth":356,"text":1229,"children":1230},"⚡ One Command: drangler preview",[1231,1232,1233],{"id":334,"depth":362,"text":335},{"id":691,"depth":362,"text":692},{"id":862,"depth":362,"text":863},{"id":929,"depth":356,"text":930},{"id":972,"depth":356,"text":973},{"id":1025,"depth":356,"text":1026},{"id":1087,"depth":356,"text":1088},{"id":1130,"depth":356,"text":1131},{"id":1149,"depth":356,"text":1150},{"id":1173,"depth":356,"text":1174},{"id":1210,"depth":356,"text":1211},[],"Move an existing Drupal site onto Drupflare without rebuilding it, check it before anything changes, and move it back whenever you like.","md","Bring the Site You Already Have","Most organisations do not need a new website. They need the one they have to stop costing a server, a patch window and a support contract. Drupflare takes an existing Drupal site as it is, checks it before anything moves, and leaves the door open in both directions.",{},"\u002Fmigrate",[],{"title":5,"description":1243},[],"migrate","PnTmTpKhl3inVz2WMh01FR2yjoUowarVCO413fIB5fQ",1793683736804]