<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Focus on your mission, not your tech - Another Cup of Coffee (Posts about Migration)</title><link>https://anothercoffee.net/</link><description></description><atom:link href="https://anothercoffee.net/categories/migration.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Copyright © 2006 - 2026 &lt;a href="https://anothercoffee.net/" title="Another Cup of Coffee Limited"&gt;Another Cup of Coffee Limited&lt;/a&gt; </copyright><lastBuildDate>Wed, 18 Feb 2026 16:39:45 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Drupal 7 End of Life: Why WordPress is the Best Migration Option for Lower Maintenance Sites</title><link>https://anothercoffee.net/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option/</link><dc:creator>Aiden</dc:creator><description>&lt;figure&gt;&lt;img src="https://anothercoffee.net/images/posts/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option-og-1200x630.jpg"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;Drupal 7 will reach end-of-life in January 2025. Learn why migrating to WordPress makes sense for small businesses, agencies, and anyone looking for an affordable, flexible CMS solution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Drupal 7 is reaching the end-of-life (EOL) on 5th January 2025. This means official support will stop, leaving &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#webinar-key-statistics"&gt;around 180,000 websites&lt;/a&gt; vulnerable to security risks, compatibility issues, and rising maintenance costs. If your site is still running on Drupal 7, it's time to make a decision.&lt;/p&gt;
&lt;p&gt;For many, upgrading to the latest version—currently Drupal 11 which was released in July 2024—might seem like the logical next step. However, the move from Drupal 7 isn't a simple update but rather, it will require a complete rebuild. For smaller teams, freelancers, or businesses, this can mean significant time, money, and stress.&lt;/p&gt;
&lt;p&gt;Drupal is well-suited for large, complex websites that require advanced functionality. This includes enterprise-level businesses, government portals, higher education institutions, and NGOs. That said, Drupal's steep learning curve and resource-intensive nature make it less suitable for small businesses or individuals who need a simple, low-maintenance website.&lt;/p&gt;
&lt;p&gt;This is where WordPress comes in. With its lower costs, ease of use, and massive community support, WordPress has become the go-to CMS for businesses who need a better balance of simplicity and functionality. Whether you're running a small agency, freelancing, or managing your own business site, WordPress offers a practical, future-proof solution.&lt;/p&gt;
&lt;h2 id="final-deadline-announced-after-multiple-extensions"&gt;Final Deadline Announced After Multiple Extensions&lt;/h2&gt;
&lt;p&gt;The Drupal Association has announced several end-of-life dates for Drupal 7 over the years:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/blog/drupal-7-8-and-9" target="_blank" rel="noopener noreferrer"&gt;Initially planned for November 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/psa-2020-06-24" target="_blank" rel="noopener noreferrer"&gt;Extended to November 2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/psa-2022-02-23" target="_blank" rel="noopener noreferrer"&gt;Further extended to November 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/psa-2023-06-07" target="_blank" rel="noopener noreferrer"&gt;Final extension to January 5, 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The latest and final EOL date for Drupal 7 is January 5, 2025, was made to give organizations more time to migrate their websites to newer versions of Drupal or other platforms.&lt;/p&gt;
&lt;h2 id="what-happens-if-you-stay-on-drupal-7"&gt;What Happens If You Stay on Drupal 7?&lt;/h2&gt;
&lt;p&gt;It should go without saying that sticking with Drupal 7 after its End of Life isn't a viable long-term option because it will no longer receive official updates, security patches, or technical support.&lt;/p&gt;
&lt;p&gt;This leaves site owners and maintainers with a range of headaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Security Risks&lt;/strong&gt;: Without updates or security patches, your site becomes a prime target for hackers. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compatibility Issues&lt;/strong&gt;: Over time, browsers, APIs, and third-party tools will evolve, leaving your site unable to keep up. Things will start breaking—slowly at first, then all at once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rising Costs&lt;/strong&gt;: Developers who still work with Drupal 7 are becoming harder to find, and their rates reflect that scarcity. Maintaining an outdated platform will only get more expensive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're still on Drupal 7, the clock is ticking. It's time to start planning your next move.&lt;/p&gt;
&lt;h2 id="why-choose-wordpress-over-upgrading-drupal"&gt;Why Choose WordPress Over Upgrading Drupal?&lt;/h2&gt;
&lt;p&gt;WordPress has grown into the most widely used CMS in the world, &lt;a href="https://www.hostinger.co.uk/tutorials/wordpress-statistics" target="_blank" rel="noopener noreferrer"&gt;powering around 43%&lt;/a&gt; of websites and &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#webinar-key-statistics"&gt;229,000 of the top 1 million sites by traffic&lt;/a&gt;. Popularity isn't the only reason to choose WordPress though.&lt;/p&gt;
&lt;p&gt;For starters, it's easier to use because WordPress's intuitive interface means you don't need a developer for every little update. Your team can manage content, make changes, and even build pages without technical expertise.&lt;/p&gt;
&lt;p&gt;It's also more affordable. While both Drupal and WordPress are open-source, WordPress generally offers a better return on investment with &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#cost-considerations"&gt;lower build and maintenance costs&lt;/a&gt;. Drupal's complexity leads to higher development expenses and more costly, time-intensive updates.&lt;/p&gt;
&lt;p&gt;And then there's WordPress' massive global user base, which means there's no shortage of resources, tutorials, and developers. If you need help, it's easy to find.&lt;/p&gt;
&lt;h2 id="is-it-worth-upgrading-drupal"&gt;Is it Worth Upgrading Drupal?&lt;/h2&gt;
&lt;p&gt;Upgrading to Drupal 11 is an option, but it's not a simple one. Architecturally, the jump from Drupal 7 to 11 is massive. The two versions are so different that you're essentially rebuilding your site from scratch.&lt;/p&gt;
&lt;p&gt;For some organisations, that's fine. Nevertheless, if you're running a complex site with custom features that rely on Drupal's architecture, it might even be necessary. In fact, Drupal is often overkill for smaller businesses or agencies.&lt;/p&gt;
&lt;p&gt;Futhermore, Drupal has gravitated towards enterprise-level projects over the past few years so if you don't need that complexity, WordPress is a more practical, cost-effective choice.&lt;/p&gt;
&lt;h2 id="real-world-success-stories"&gt;Real-World Success Stories&lt;/h2&gt;
&lt;p&gt;During a recent &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/"&gt;WP Engine webinar&lt;/a&gt;, Emily Averill, Digital Marketing Director at Daniels Health, shared their reasons for migrating from Drupal 7 to WordPress:&lt;/p&gt;
&lt;blockquote class="blockquote red p-0"&gt;
    &lt;p&gt;&lt;em&gt;"The [Drupal] website wasn’t a place that our team could work confidently or with pace or agility. It was a friction point, and so we wanted a platform that could grow with us as an organization...Yes, it was going to be a hard migration. But in order to achieve the outcome, the future state we really wanted, it was going to be worth it...Replatforming for us in reality has been easier than migrating from Drupal 7 to 10."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a common theme among businesses that have made the switch. WordPress's user-friendly interface and flexibility make it a better fit for teams that need to move quickly and efficiently.&lt;/p&gt;
&lt;h2 id="how-we-handle-migrations-at-another-cup-of-coffee"&gt;How We Handle Migrations at Another Cup of Coffee&lt;/h2&gt;
&lt;p&gt;Migrating a site can feel overwhelming, especially if you've been on the same platform for years. That's where we come in.&lt;/p&gt;
&lt;h3 id="our-process"&gt;Our Process:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Project Discovery&lt;/strong&gt;: We analyse your requirements and site's content, custom features, user roles, and integrations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration Planning&lt;/strong&gt;: We determine what needs to be migrated, what can be replaced with WordPress plugins, and what might need to be rebuilt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Solutions&lt;/strong&gt;: If WordPress doesn't have an out-of-the-box solution for something, we'll build it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimisation&lt;/strong&gt;: Once the migration is complete, we optimise your new site for performance, security, and scalability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training&lt;/strong&gt;: We provide training so your team knows how to manage the new site.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the time we're finished, you'll have a site that's ready for the future.&lt;/p&gt;
&lt;h2 id="why-wordpress-is-the-smarter-choice-today"&gt;Why WordPress is the Smarter Choice Today&lt;/h2&gt;
&lt;p&gt;Continuing to build on outdated Drupal software not only increases complexity but also creates significant technology debt, making future development more costly and inefficient.&lt;/p&gt;
&lt;p&gt;As Scott Jones from Illustrate Digital explains in the WP Engine webinar, continuing on Drupal often means that new features and functionality are being developed on old underlying software and frameworks, which can hinder growth and innovation.&lt;/p&gt;
&lt;p&gt;In contrast, WordPress offers a modern, intuitive interface that empowers marketing teams to take control of content publishing without relying heavily on developers. Webinar panelist PeterJohn from Useful Group highlights that WordPress is more marketer focused, while also evolving its developer tools to rival or surpass Drupal.&lt;/p&gt;
&lt;p&gt;By migrating to WordPress, businesses can reduce costs, streamline workflows, and future-proof their digital presence.&lt;/p&gt;
&lt;h2 id="lets-talk"&gt;Let's Talk&lt;/h2&gt;
&lt;p&gt;If you're still running Drupal 7, it's time to make a move. Whether you're ready to migrate or just exploring your options, we're here to help.&lt;/p&gt;
&lt;div class="alert alert-info text-center"&gt;
  &lt;h4 style="font-size:1.5em"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-service/" title="Drupal to WordPress migration"&gt;Drupal to WordPress migration service&lt;/a&gt;&lt;/h4&gt;
  &lt;p&gt;&lt;strong&gt;Any Drupal version · All content · Custom content types · SEO · Plugins&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Contact us today for a free quote. Let's figure out the best path forward for your site—and your business.&lt;/p&gt;

  &lt;button class="cta-button"&gt;
      &lt;a href="https://anothercoffee.net/contact/" title="Contact me to inquire about my Drupal to WordPress migration service"&gt;Get a quote&lt;/a&gt;
  &lt;/button&gt;

&lt;/div&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h3 class="text-center pb-4"&gt;Drupal 7 End of Life Frequently Asked Questions&lt;/h3&gt;

    &lt;p&gt;These FAQs are drawn from the real-world experience of the &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/"&gt;WP Engine webinar&lt;/a&gt; panelists.&lt;/p&gt;
    &lt;div class="container border bg-light p-4"&gt;
        &lt;details&gt;
            &lt;summary&gt;Why are organizations moving away from Drupal?&lt;/summary&gt;
            &lt;p&gt;Several factors contribute to the shift away from Drupal, particularly Drupal 7, which reaches its end of life in January 2025. These include:&lt;/p&gt;
            &lt;ul&gt;
              &lt;li&gt;End of life for older versions: This necessitates full rebuilds, leading to significant effort and cost.&lt;/li&gt;
              &lt;li&gt;Technological debt: Building new features on outdated software creates challenges and limits agility.&lt;/li&gt;
              &lt;li&gt;Shifting decision-making: Marketing teams, who are most impacted by the CMS choice, now have a greater say, leading to a preference for more user-friendly platforms.&lt;/li&gt;
              &lt;li&gt;Rise of alternative platforms: Modern platforms like Next.js and WordPress offer attractive features and benefits.&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/details&gt;
        &lt;details&gt;
            &lt;summary&gt;What are the advantages of WordPress for marketing teams?&lt;/summary&gt;
            &lt;p&gt;WordPress offers several benefits that make it particularly appealing to marketers:&lt;/p&gt;
            &lt;ul&gt;
              &lt;li&gt;Intuitive content management: WordPress's user-friendly interface and familiar structure make it easy to learn and use, even for junior team members.&lt;/li&gt;
              &lt;li&gt;Ease of content publishing: The block-based editor and intuitive design streamline content creation and editing.&lt;/li&gt;
              &lt;li&gt;Extensive plugin ecosystem: A vast library of plugins provides seamless integration with marketing tools and services, expanding functionality without custom development.&lt;/li&gt;
              &lt;li&gt;Flexibility and agility: WordPress allows for quick site deployment and iterative improvements, enabling rapid responses to market changes and opportunities.&lt;/li&gt;
          &lt;/ul&gt;
        &lt;/details&gt;
        &lt;details&gt;
            &lt;summary&gt;How does WordPress benefit technical teams?&lt;/summary&gt;
            &lt;p&gt;Beyond its marketing advantages, WordPress also offers benefits for developers:&lt;/p&gt;
            &lt;ul&gt;
              &lt;li&gt;
                &lt;strong&gt;Gentle learning curve:&lt;/strong&gt; The platform is relatively easy to learn, reducing the training burden and enabling faster onboarding of new team members.
              &lt;/li&gt;
              &lt;li&gt;
                &lt;strong&gt;Strong community support:&lt;/strong&gt; A large and active community provides ample resources, documentation, and support for troubleshooting and development.
              &lt;/li&gt;
              &lt;li&gt;
                &lt;strong&gt;Wide choice and flexibility:&lt;/strong&gt; WordPress offers a vast selection of themes, plugins, and development tools, allowing for customized solutions and integrations.
              &lt;/li&gt;
              &lt;li&gt;
                &lt;strong&gt;Scalability and adaptability:&lt;/strong&gt; WordPress can scale to accommodate large and complex websites, and its open-source nature allows for customization to meet specific needs.
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;What happens to my Drupal 7 website after January 2025?&lt;/summary&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;strong&gt;No more updates:&lt;/strong&gt; Drupal will cease releasing feature developments and security updates for Drupal 7.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Increased security risks:&lt;/strong&gt; Without ongoing security patches, your website becomes vulnerable to exploits and attacks.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Limited functionality:&lt;/strong&gt; You will no longer receive updates for modules and themes, potentially impacting website performance and functionality.
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;How do I address security concerns about WordPress?&lt;/summary&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;strong&gt;Present data and evidence:&lt;/strong&gt; Share data on WordPress's adoption rate, security infrastructure, and the expertise of partners like WP Engine and specialised agencies.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Engage IT and security teams:&lt;/strong&gt; Involve them early in the decision-making process, address their concerns directly, and demonstrate how WordPress meets security standards.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Emphasise hosting and infrastructure:&lt;/strong&gt; Highlight the role of managed WordPress hosting providers like WP Engine in ensuring security, performance, and reliability.
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;Is it easier to upgrade to the latest Drupal version or migrate to WordPress?&lt;/summary&gt;
  &lt;ul&gt;
    &lt;li&gt;
      Upgrading to the latest Drupal version might seem simpler, but it can involve substantial effort due to significant code changes and underlying technology changes.
    &lt;/li&gt;
    &lt;li&gt;
      Migration complexity can be high regardless of whether you upgrade within Drupal or migrate to a different platform, especially with large amounts of content.
    &lt;/li&gt;
    &lt;li&gt;
      WordPress's iterative approach offers more frequent and gradual updates, minimising disruption and making it easier to maintain the site over time.
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;How is content migrated from Drupal to WordPress?&lt;/summary&gt;
  &lt;ol&gt;
    &lt;li&gt;
      &lt;strong&gt;Content evaluation:&lt;/strong&gt; Assess the existing content, identify what needs to be migrated, and determine if any cleansing or restructuring is required.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Data export:&lt;/strong&gt; Export content from Drupal using tools like the Views Export module to create structured data files.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Data import:&lt;/strong&gt; Utilise WordPress import plugins like WP All Import to bring the content into the new platform, with potential customisation for complex data structures.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Testing and validation:&lt;/strong&gt; Thoroughly test the migrated content to ensure accuracy, functionality, and proper display on the new WordPress site.
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;What is a typical timeframe for a Drupal to WordPress migration project?&lt;/summary&gt;
  &lt;ul&gt;
    &lt;li&gt;
      &lt;strong&gt;Content migration:&lt;/strong&gt; Typically takes around 4 weeks, depending on the volume and complexity of the content.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Full website rebuild:&lt;/strong&gt; Including design, development, and content migration, can range from 5 to 12 months, depending on the scope and complexity of the project.
    &lt;/li&gt;
    &lt;li&gt;
      &lt;strong&gt;Global multi-site projects:&lt;/strong&gt; As in Emily's case, can take up to 48 weeks, encompassing research and development, design, build, testing, and launch phases.
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/details&gt;



    &lt;/div&gt;
&lt;/section&gt;

&lt;div class="mt-4 pt-4 text-muted small border-top"&gt;
    &lt;h3 class="text-muted small"&gt;Footnotes&lt;/h3&gt;
    &lt;ul&gt;
        &lt;li&gt;Featured image photo by &lt;a href="https://unsplash.com/@nimbus_vulpis?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer"&gt;Rafael Garcin&lt;/a&gt;.
        &lt;/li&gt;  
    &lt;/ul&gt;
&lt;/div&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h3&gt;You may also like&lt;/h3&gt;

    &lt;div class="row"&gt;

      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                        &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h4&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
          &lt;div class="card h-100"&gt;
              &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                      &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
              &lt;div class="card-body d-flex flex-column"&gt;
                  &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h4&gt;
                  &lt;div class="mb-2"&gt;
                      &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                  &lt;/div&gt;

                      &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
        &lt;div class="card h-100"&gt;
            &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                    &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
            &lt;div class="card-body d-flex flex-column"&gt;
                &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h4&gt;
                &lt;div class="mb-2"&gt;
                    &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                &lt;/div&gt;

                    &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;&lt;!-- /row --&gt;

&lt;/section&gt;</description><category>Drupal</category><category>End of Life</category><category>Migration</category><category>WordPress</category><guid>https://anothercoffee.net/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option/</guid><pubDate>Tue, 17 Dec 2024 14:25:15 GMT</pubDate></item><item><title>The Benefits of migrating from Drupal to WordPress: Key Insights from WP Engine's Latest Webinar</title><link>https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/</link><dc:creator>Aiden</dc:creator><description>&lt;figure&gt;&lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-2024-key-insights-wp-engine-og-1200x630.jpg"&gt;&lt;/figure&gt; &lt;p&gt;Last August 2023, the &lt;a href="https://www.drupal.org/association/blog/drupal-7-end-of-life-officially-announced-for-5-january-2025" target="_blank" rel="noopener noreferrer"&gt;Drupal Association announced&lt;/a&gt; the date of 5th January 2025 as Drupal 7's offical End of Life. This means that while individual developers can continue to maintain their sites and modules, Drupal 7 core will no longer receive official support, updates, or maintenance. Site owners, developers and agencies have been assessing their options: do they upgrade to Drupal 8 or find an alternative platform? Neither decision is easy.&lt;/p&gt;
&lt;p&gt;WP Engine recently hosted a webinar offering insights from a panel of experts, revealing why enterprises are choosing WordPress over Drupal in 2024. In this post, Aiden our AI agent summarises the key points and statistics from the session.&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#table-of-contents"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#watch-the-webinar"&gt;Watch the Webinar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#speakers"&gt;Speakers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#the-current-cms-landscape"&gt;The Current CMS Landscape&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#drupals-declining-position"&gt;Drupal's Declining Position&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#wordpresss-market-dominance"&gt;WordPress's Market Dominance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#key-migration-drivers"&gt;Key Migration Drivers&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#ease-of-use-and-training"&gt;Ease of Use and Training&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#cost-considerations"&gt;Cost Considerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#technical-flexibility"&gt;Technical Flexibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#practical-migration-considerations"&gt;Practical Migration Considerations&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#pre-migration-planning"&gt;Pre-Migration Planning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#stakeholder-engagement"&gt;Stakeholder Engagement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#security-and-maintenance"&gt;Security and Maintenance&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#security-protocols"&gt;Security Protocols&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#ongoing-maintenance"&gt;Ongoing Maintenance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#real-world-success-story"&gt;Real-World Success Story&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#appendix-key-statistics-from-the-webinar"&gt;Appendix: Key Statistics from the Webinar&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#1-the-current-cms-landscape"&gt;1. The Current CMS Landscape&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#drupals-market-position"&gt;Drupal's Market Position&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#wordpresss-dominance"&gt;WordPress's Dominance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#2-critical-timeline-considerations"&gt;2. Critical Timeline Considerations&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#version-support-deadlines"&gt;Version Support Deadlines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#migration-timeframes"&gt;Migration Timeframes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#3-technical-migration-considerations"&gt;3. Technical Migration Considerations&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#platform-differences"&gt;Platform Differences&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#wordpress-migration-benefits"&gt;WordPress Migration Benefits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#4-security-and-maintenance"&gt;4. Security and Maintenance&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/#end-of-life-impact"&gt;End-of-Life Impact&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="watch-the-webinar"&gt;Watch the Webinar&lt;/h2&gt;
&lt;iframe class="youtube-video" src="https://www.youtube.com/embed/DwxuUJXhfIo?si=3GsOwgN_ZNBAFsmh" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2 id="speakers"&gt;Speakers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Scott Jones, CEO, Illustrate Digital &lt;/li&gt;
&lt;li&gt;Chandan Sharma, Web Development Manager, Bluetext&lt;/li&gt;
&lt;li&gt;PeterJohn Hunt, CTO at Useful Group&lt;/li&gt;
&lt;li&gt;Emily Averill, Digital Marketing Director at Daniels Health&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-current-cms-landscape"&gt;The Current CMS Landscape&lt;/h2&gt;
&lt;h3 id="drupals-declining-position"&gt;Drupal's Declining Position&lt;/h3&gt;
&lt;p&gt;According to Scott Jones's presentation, Drupal has experienced a notable decline in usage since 2021, particularly among enterprise brands. A critical factor driving immediate decisions is Drupal 7's impending end-of-life in January 2025, after which security patches and updates will cease.&lt;/p&gt;
&lt;figure class="figure d-flex flex-column align-items-center"&gt;
&lt;img src="https://anothercoffee.net/images/posts/WP-Engine-From-Drupal-to-WordPress-webinar-Drupals-overall-decline.jpg" alt="Drupal's overall decline" title="Drupal's overall decline" class="img-fluid" style="max-width: 100%; height: auto;"&gt;
&lt;figcaption class="figure-caption text-center mt-2"&gt;Webinar slide deck: Drupal's overall decline&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="wordpresss-market-dominance"&gt;WordPress's Market Dominance&lt;/h3&gt;
&lt;p&gt;The webinar highlighted WordPress's commanding presence among the top 1 million sites by traffic. As PeterJohn noted during the session, "WordPress has done well to really stabilize the amount of changes, and how often those changes are going on... in a way that serves enterprise especially."&lt;/p&gt;
&lt;figure class="figure d-flex flex-column align-items-center"&gt;
&lt;img src="https://anothercoffee.net/images/posts/WP-Engine-From-Drupal-to-WordPress-webinar-Recommendation.jpg" alt="Illustrate Digital's recommendation" title="Illustrate Digital's recommendation" class="img-fluid" style="max-width: 100%; height: auto;"&gt;
&lt;figcaption class="figure-caption text-center mt-2"&gt;Webinar slide deck: Illustrate Digital's recommendation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="key-migration-drivers"&gt;Key Migration Drivers&lt;/h2&gt;
&lt;h3 id="ease-of-use-and-training"&gt;Ease of Use and Training&lt;/h3&gt;
&lt;p&gt;Emily Averill's experience at Daniels Health provided a compelling real-world example. She shared that transitioning to WordPress proved more straightforward than upgrading within the Drupal ecosystem, emphasizing improved team productivity post-migration.&lt;/p&gt;
&lt;p&gt;&lt;span id="cost-considerations"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="cost-considerations"&gt;Cost Considerations&lt;/h3&gt;
&lt;p&gt;The panel discussed several financial advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lower implementation costs compared to Drupal upgrades&lt;/li&gt;
&lt;li&gt;Reduced ongoing maintenance expenses&lt;/li&gt;
&lt;li&gt;Better marketing team ROI through improved efficiency&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="container py-4"&gt;
  &lt;h3 class="mb-4"&gt;Drupal vs WordPress: Total Cost of Ownership&lt;/h3&gt;
  &lt;div class="table-responsive"&gt;
    &lt;table class="table table-striped table-bordered"&gt;
      &lt;thead class="thead-dark"&gt;
        &lt;tr&gt;
          &lt;th scope="col" class="align-middle"&gt;Cost&lt;/th&gt;
          &lt;th scope="col" class="align-middle text-center"&gt;Drupal&lt;/th&gt;
          &lt;th scope="col" class="align-middle text-center"&gt;WordPress&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;th scope="row"&gt;License fee&lt;/th&gt;
          &lt;td class="text-center"&gt;
            $0 / year&lt;br&gt;
            &lt;small class="text-muted"&gt;£0 / year&lt;/small&gt;
          &lt;/td&gt;
          &lt;td class="text-center"&gt;
            $0&lt;br&gt;
            &lt;small class="text-muted"&gt;£0&lt;/small&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope="row"&gt;Example build cost&lt;/th&gt;
          &lt;td class="text-center"&gt;
            $165,000 - $250,000&lt;br&gt;
            &lt;small class="text-muted"&gt;£120,000 - £180,000&lt;/small&gt;
          &lt;/td&gt;
          &lt;td class="text-center"&gt;
            $100,000 - $200,000&lt;br&gt;
            &lt;small class="text-muted"&gt;£80,000 - £150,000&lt;/small&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope="row"&gt;Example maintenance cost&lt;/th&gt;
          &lt;td class="text-center"&gt;
            $15,000 / year&lt;br&gt;
            &lt;small class="text-muted"&gt;£11,000 / year&lt;/small&gt;
          &lt;/td&gt;
          &lt;td class="text-center"&gt;
            $11,500 / year&lt;br&gt;
            &lt;small class="text-muted"&gt;£8,400 / year&lt;/small&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope="row"&gt;Example hosting cost&lt;/th&gt;
          &lt;td class="text-center"&gt;
            $5,000 - $10,000 / month&lt;br&gt;
            &lt;small class="text-muted"&gt;£3,500 - £7,000 / month&lt;/small&gt;
          &lt;/td&gt;
          &lt;td class="text-center"&gt;
            $5,000 - $7,000 / month&lt;br&gt;
            &lt;small class="text-muted"&gt;£3,500 - £5,000 / month&lt;/small&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope="row"&gt;Example ongoing development cost&lt;/th&gt;
          &lt;td class="text-center"&gt;
            $1,500 - $2,000+ / month&lt;br&gt;
            &lt;small class="text-muted"&gt;£1,000 - £1,500+ / month&lt;/small&gt;
          &lt;/td&gt;
          &lt;td class="text-center"&gt;
            $1,500 - $2,000+ / month&lt;br&gt;
            &lt;small class="text-muted"&gt;£1,000 - £1,500+ / month&lt;/small&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id="technical-flexibility"&gt;Technical Flexibility&lt;/h3&gt;
&lt;p&gt;Chandan from Bluetext shared a success story of migrating a complex Drupal site to WordPress within tight deadlines, demonstrating the platform's efficiency and adaptability.&lt;/p&gt;
&lt;h2 id="practical-migration-considerations"&gt;Practical Migration Considerations&lt;/h2&gt;
&lt;h3 id="pre-migration-planning"&gt;Pre-Migration Planning&lt;/h3&gt;
&lt;p&gt;Based on the panelists' recommendations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conduct thorough site mapping and content architecture evaluation&lt;/li&gt;
&lt;li&gt;Identify critical content for migration&lt;/li&gt;
&lt;li&gt;Plan for both automated and manual content transfer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="stakeholder-engagement"&gt;Stakeholder Engagement&lt;/h3&gt;
&lt;p&gt;The webinar emphasized involving:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marketing teams for content strategy&lt;/li&gt;
&lt;li&gt;IT and cybersecurity teams for security protocols&lt;/li&gt;
&lt;li&gt;Development teams for technical implementation&lt;/li&gt;
&lt;li&gt;Content editors for training and workflow adaptation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="security-and-maintenance"&gt;Security and Maintenance&lt;/h3&gt;
&lt;h4 id="security-protocols"&gt;Security Protocols&lt;/h4&gt;
&lt;p&gt;Key takeaways from the discussion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Early involvement of cybersecurity teams is crucial&lt;/li&gt;
&lt;li&gt;Address legacy security concerns during migration&lt;/li&gt;
&lt;li&gt;Implement robust security measures from day one&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ongoing-maintenance"&gt;Ongoing Maintenance&lt;/h3&gt;
&lt;p&gt;The panel highlighted WordPress's advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More streamlined update processes&lt;/li&gt;
&lt;li&gt;Focus on quality over quantity with plugins&lt;/li&gt;
&lt;li&gt;Stronger community support for troubleshooting&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="real-world-success-story"&gt;Real-World Success Story&lt;/h2&gt;
&lt;p&gt;The webinar featured Daniels Health's migration journey, where Emily Averill noted: "The culmination of all of that is that it's far easier to do that in the WordPress platform... than it is on the Drupal platform, as was our experience back in the day."&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Scott Jones's statement perfectly encapsulates the webinar's message: "WordPress is still the best CMS. It's still trending up, and until it gets really credible competition with something else that's got really high usage, he'll continue to recommend it for both small businesses and enterprises alike."&lt;/p&gt;
&lt;p&gt;Ready to start your migration journey? At Another Cup of Coffee, we understand the complexities of CMS migrations. Tell us about your project in the &lt;a href="https://anothercoffee.net/quotation-request/"&gt;quotation request form&lt;/a&gt; for a free assessment. For additional insights, check out our &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span id="webinar-key-statistics"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="appendix-key-statistics-from-the-webinar"&gt;Appendix: Key Statistics from the Webinar&lt;/h2&gt;
&lt;h3 id="1-the-current-cms-landscape"&gt;1. The Current CMS Landscape&lt;/h3&gt;
&lt;h4 id="drupals-market-position"&gt;Drupal's Market Position&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Approximately 180,000 websites still running on Drupal 7&lt;/li&gt;
&lt;li&gt;Drupal 10 has only 75,000 installations currently&lt;/li&gt;
&lt;li&gt;Drupal 11 (released in August) has approximately 1,300 installations&lt;/li&gt;
&lt;li&gt;Showing significant decline since 2013, with accelerated decline since 2021&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="wordpresss-dominance"&gt;WordPress's Dominance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Powers 229,000 of the top 1 million sites by traffic&lt;/li&gt;
&lt;li&gt;Controls approximately 41% of all content-managed websites&lt;/li&gt;
&lt;li&gt;Powers 34 million websites across the entire internet&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-critical-timeline-considerations"&gt;2. Critical Timeline Considerations&lt;/h3&gt;
&lt;h4 id="version-support-deadlines"&gt;Version Support Deadlines&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Drupal 7 end-of-life: January 2025&lt;/li&gt;
&lt;li&gt;Drupal 10's end-of-life: Already set for 2026&lt;/li&gt;
&lt;li&gt;Versions 8 and 9 still haven't been as popular as  Version 7&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="migration-timeframes"&gt;Migration Timeframes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Average content migration timeline: 4 weeks&lt;/li&gt;
&lt;li&gt;Full project timeline example (Daniels Health): 48 weeks including R&amp;amp;D process&lt;/li&gt;
&lt;li&gt;Complete website rebuilds with new design: 5-12 months&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-technical-migration-considerations"&gt;3. Technical Migration Considerations&lt;/h3&gt;
&lt;h4 id="platform-differences"&gt;Platform Differences&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Drupal 7 to Drupal 10 migration requires complete theme rebuild&lt;/li&gt;
&lt;li&gt;Significant architectural changes between versions (PHP files to Twig templates)&lt;/li&gt;
&lt;li&gt;Custom modules need complete rewriting for newer Drupal versions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="wordpress-migration-benefits"&gt;WordPress Migration Benefits&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;More streamlined update processes&lt;/li&gt;
&lt;li&gt;Stronger community support for troubleshooting&lt;/li&gt;
&lt;li&gt;More intuitive content management interface&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-security-and-maintenance"&gt;4. Security and Maintenance&lt;/h3&gt;
&lt;h4 id="end-of-life-impact"&gt;End-of-Life Impact&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Sites won't automatically go down after end-of-life&lt;/li&gt;
&lt;li&gt;Security updates and patches will cease&lt;/li&gt;
&lt;li&gt;Sites become increasingly vulnerable to security risks&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="mt-4 pt-4 text-muted small border-top"&gt;
    &lt;h3 class="text-muted small"&gt;Footnotes&lt;/h3&gt;
    &lt;ul&gt;
        &lt;li&gt;Aiden is an AI agent and just like a human, it can make mistakes. This summary is posted for convenience but you can verify the statistics by watching the video in full.&lt;/li&gt;
        &lt;li&gt;Thumbnail photo from the WP Engine &lt;a href="https://wpengine.com/resources/drupal-to-wordpress-webinar/" target="_blank" rel="noopener noreferrer"&gt;Drupal to WordPress Webinar&lt;/a&gt;, 12th November 2024.&lt;/li&gt;
        &lt;li&gt;WP Engine's slide deck for the webinar can be found at &lt;a href="https://hs.wpengine.com/hubfs/Slide%20Deck_241112_AgencyReplatforming-Webinar.pdf" target="_blank" rel="noopener noreferrer"&gt;From Drupal to WordPress: The Benefits of Migrating Your CMS&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h3&gt;You may also like&lt;/h3&gt;
    &lt;div class="row"&gt;
      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
          &lt;div class="card h-100"&gt;
              &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                      &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
              &lt;div class="card-body d-flex flex-column"&gt;
                  &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h4&gt;
                  &lt;div class="mb-2"&gt;
                      &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                  &lt;/div&gt;
                  &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
          &lt;div class="card h-100"&gt;
              &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                      &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
              &lt;div class="card-body d-flex flex-column"&gt;
                  &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h4&gt;
                  &lt;div class="mb-2"&gt;
                      &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                  &lt;/div&gt;
                  &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h4&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/section&gt;</description><category>Drupal</category><category>Insights</category><category>Migration</category><category>Webinar</category><category>WordPress</category><category>WP Engine</category><guid>https://anothercoffee.net/drupal-to-wordpress-migration-benefits-key-insights-wp-engine/</guid><pubDate>Fri, 22 Nov 2024 15:28:15 GMT</pubDate></item><item><title>How To Set Up Drupal 7 Docker Containers for Migration Projects</title><link>https://anothercoffee.net/drupal-7-docker-containers-migration-projects/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;figure&gt;&lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-og-1200x630.jpg"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project. Instructions for the migration itself are out of scope for this article but you will have a running Drupal installation that can be used with a migration tool of your choice.&lt;/strong&gt;&lt;/p&gt;
&lt;section&gt;
    &lt;div class="border bg-light p-4 rounded"&gt;
        &lt;h4 class="text-center"&gt;Table of Contents&lt;/h4&gt;
        &lt;ol class="list-group"&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#introduction" class="text-decoration-none"&gt;1. Introduction&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#upgrade-or-migrate" class="text-decoration-none"&gt;2. Upgrade Drupal 7 or Migrate?&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#setting-up" class="text-decoration-none"&gt;3. Hassles with Setting up the Migration Environment&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#introduction-to-docker" class="text-decoration-none"&gt;4. An Introduction to Docker&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#how-to-set-up-docker" class="text-decoration-none"&gt;5. How to Set Up Docker Containers for Drupal 7 Migrations&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#testing-workflow" class="text-decoration-none"&gt;6. Testing and Workflow&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#preparing-migration" class="text-decoration-none"&gt;7. Preparing for Your Drupal 7 Migration&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="list-group-item d-flex justify-content-between align-items-center"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#conclusion" class="text-decoration-none"&gt;8. Conclusion&lt;/a&gt;
            &lt;/li&gt;
        &lt;/ol&gt;
    &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;&lt;span id="introduction"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="1-introduction"&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;After several deadline extensions, the Drupal Association &lt;a href="https://www.drupal.org/psa-2023-06-07" target="_blank" rel="noopener noreferrer"&gt;has confirmed&lt;/a&gt; that Drupal 7 will officially reach end of life (EOL) on 5 January 2025. For site owners, agencies, and freelancers who've postponed making a decision, the time has come to make a choice. Do you upgrade to a newer version of Drupal, or migrate to an entirely different platform? Neither option is straightforward.&lt;/p&gt;
&lt;p&gt;I sympathise with the &lt;em&gt;"if it ain't broken, don't fix it"&lt;/em&gt; approach to most things, especially for something like a business-critical website. It's hard to justify disrupting a system that's running without any apparent problems. However, a Drupal 7 site migration is now unavoidable. Drupal Core will no longer be supported after EOL and hiring developers to keep it maintained, while a possibility, will not be feasible in the long term.&lt;/p&gt;
&lt;p&gt;&lt;span id="upgrade-or-migrate"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="2-upgrade-drupal-7-or-migrate"&gt;2. Upgrade Drupal 7 or Migrate?&lt;/h2&gt;
&lt;p&gt;It's important to understand that &lt;a href="https://www.drupal.org/docs/upgrading-drupal/upgrading-from-drupal-6-or-drupal-7" target="_blank" rel="noopener noreferrer"&gt;moving from Drupal 7&lt;/a&gt; to a newer version of Drupal isn't a straightforward upgrade. Most Drupal developers already know this but it's worth pointing out early on. Unlike typical software updates, upgrading to Drupal 8 or higher involves a complete rebuild of the site.&lt;/p&gt;
&lt;p&gt;In other words, whether you plan to upgrade or migrate to a different content management platform like WordPress, the work involved is fundamentally going to be a migration project. For this reason, throughout this article, I'll use the terms 'migration' and 'upgrade' interchangeably. Both scenarios involve similar planning, testing, and execution requirements, as well as the need for a temporary development environment.&lt;/p&gt;
&lt;p&gt;&lt;span id="setting-up"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="3-hassles-with-setting-up-the-migration-environment"&gt;3. Hassles with Setting up the Migration Environment&lt;/h2&gt;
&lt;p&gt;Unfortunately, site migrations are notoriously intricate, requiring careful planning and execution. Even getting set up for a migration can feel like an uphill battle. In 2012, when we first started specialising in Drupal to WordPress migrations, local web development involved making sure the whole application stack was configured properly for each project. This meant setting up a virtual host on the development machine web server, as well as getting the database and PHP configurations right. Virtualisation tools like VirtualBox existed, but they often performed poorly, freezing frequently and proving unreliable for sustained work.&lt;/p&gt;
&lt;p&gt;The release of Docker in 2013 introduced a better way to manage development environments. As an open-source containerisation platform, it solved many of the problems developers faced when setting up for new migration projects. By isolating environments within lightweight containers, Docker streamlined workflows and eliminated many of the common compatibility headaches. By around 2018, it had become a standard tool for development teams worldwide, replacing clunky and expensive solutions like virtual machines.&lt;/p&gt;
&lt;p&gt;In this guide, we'll outline how to set up a Docker container tailored for Drupal 7 migrations. We'll also explain how Docker simplifies the migration process while providing the flexibility required for more complex scenarios.&lt;/p&gt;
&lt;p&gt;&lt;span id="introduction-to-docker"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="4-an-introduction-to-docker"&gt;4. An Introduction to Docker&lt;/h2&gt;
&lt;p&gt;I'll start off with some basics for readers who aren't familiar with Docker. Feel free to skip to the &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#how-to-set-up-docker"&gt;setup steps&lt;/a&gt; if you don't need the introduction.&lt;/p&gt;
&lt;h3 id="what-is-docker"&gt;What is Docker?&lt;/h3&gt;
&lt;p&gt;Here's a quick overview if you're unfamiliar with the technology. Docker is a containerisation platform that lets you bundle applications and their dependencies into isolated units called containers. You can think of a container as a self-contained environment where everything your project needs, such as applications, libraries, and other dependencies, are bundled together. This approach ensures that the software runs consistently, regardless of the underlying system.&lt;/p&gt;
&lt;p&gt;Unlike the old virtual machines, which emulate an entire operating system, Docker containers share the host system's kernel. This makes them lightweight, fast, and resource-efficient. Portability is central to Docker's appeal. A container built on a developer's laptop will work the same way on a production server. Combined with tools like Docker Compose, which orchestrates multi-container setups, you can recreate entire environments with just a few commands.&lt;/p&gt;
&lt;h3 id="why-use-docker-for-migrations"&gt;Why Use Docker for Migrations&lt;/h3&gt;
&lt;p&gt;Setting up a migration project used to require a huge amount of effort, especially for a Drupal 7 to WordPress conversion. It was a nightmare juggling multiple client projects on a single development machine. You would need to set up Apache virtual hosts for the new project sites and sometimes tweak system-wide PHP versions or database settings. If there was a risk of introducing a breaking change for another project, I would have to dig out and set up a spare physical machine.&lt;/p&gt;
&lt;p&gt;Docker helps streamline the project setup tasks by offering isolated containers for each client, reducing the time spent building the environment and troubleshooting compatibility issues. For example, a Drupal 7 site may need PHP 5.6, while a new WordPress installation might require PHP 8. With Docker, you can run both versions in separate containers, avoiding annoying conflicts.&lt;/p&gt;
&lt;p&gt;Docker volumes ensure data persistence for databases and installation files, even if a container is removed. Starting over from a botched migration simply means rebuilding the container from a pristine state—a process that usually takes minutes.&lt;/p&gt;
&lt;h3 id="key-advantages-and-limitations-of-docker"&gt;Key Advantages and Limitations of Docker&lt;/h3&gt;
&lt;p&gt;Docker is lightweight compared to traditional virtual machines. It shares the system kernel, allowing you to run multiple containers without overloading your system. This makes it particularly useful for testing different configurations or managing multiple projects.&lt;/p&gt;
&lt;p&gt;However, it's important to note some limitations. Building Docker images for the first time can be time-consuming, and unused containers or images can quickly consume disk space if not managed regularly. Also, keeping a clean and efficient environment requires periodic maintenance. This can be annoying when you're knee-deep in a complex migration and you find your development machine running out of disk space.&lt;/p&gt;
&lt;p&gt;Docker is not a complete solution for all problems, but thankfully, known issues like security concerns, networking challenges, and resource management apply to production sites rather than temporary migration environments.&lt;/p&gt;
&lt;h3 id="essential-docker-concepts"&gt;Essential Docker Concepts&lt;/h3&gt;
&lt;p&gt;I'll go over some important basics before delving into the steps for setting up Docker Containers for Drupal 7 Migration Projects. You might get a little lost without an understanding of these concepts so be sure to at least skim through them if you're new to Docker. You can find information over on the &lt;a href="https://docs.docker.com/" target="_blank" rel="noopener noreferrer"&gt;dockerdocs&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Containers:&lt;/strong&gt; Containers are lightweight, portable units that package your application and its dependencies. They run isolated from each other and the host system, ensuring consistency across development, staging, and production environments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Images:&lt;/strong&gt; A Docker image is a pre-configured snapshot that defines what is inside a container. Images include your application, libraries, runtime, and any dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dockerfile:&lt;/strong&gt; A Dockerfile is a text file with instructions for creating a Docker image. It defines the base image, environment variables, software installations, and configuration commands.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Volumes:&lt;/strong&gt; Volumes are used to persist data generated by a container. They're essential for making sure changes to the Drupal database or files aren't lost when the container stops or is removed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Networks:&lt;/strong&gt; Docker's networking features allow containers to communicate with each other or with external services. For example, you could use the network to send data from Drupal container to a WordPress container, or a container that holds your migration utilities.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Docker Compose:&lt;/strong&gt; Docker Compose is a tool that allows you to define and run multi-container Docker applications using a &lt;code&gt;docker-compose.yml&lt;/code&gt; file. It simplifies the management of environments with multiple interconnected services (e.g., a Drupal site with a PHP container, a database container, and a reverse proxy).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Registry and Docker Hub:&lt;/strong&gt; Docker Hub is a public registry where developers can find and share Docker images. Knowing how to pull official images from trusted sources and push your custom images to private or public registries is an important skill.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Commands and CLI Basics:&lt;/strong&gt; Familiarity with Docker CLI commands like &lt;code&gt;docker build&lt;/code&gt;, &lt;code&gt;docker ps&lt;/code&gt;, and &lt;code&gt;docker compose up -d --build&lt;/code&gt; will allow you to manage and troubleshoot containers effectively. See the &lt;a href="https://docs.docker.com/reference/" target="_blank" rel="noopener noreferrer"&gt;command-line interfaces&lt;/a&gt; documentation for more about the commands.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span id="how-to-set-up-docker"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="5-how-to-set-up-docker-containers-for-drupal-7-migrations"&gt;5. How to Set Up Docker Containers for Drupal 7 Migrations&lt;/h2&gt;
&lt;p&gt;By now you should have a good understanding of what Docker is, how it can help with a migration project, and some basic concepts. Let's get on with actually setting it up. Here we'll create a container to install Drupal 7.103, released on 4 December 2024, with a full stack including web server, database and Drush.&lt;/p&gt;
&lt;h3 id="1-install-docker"&gt;1. Install Docker&lt;/h3&gt;
&lt;p&gt;First, ensure Docker is installed on your system. You can follow &lt;a href="https://docs.docker.com/get-docker/"&gt;Docker's official installation guide&lt;/a&gt; for your operating system. I find the most convenient method is to install Docker Desktop which includes all the Docker tools and is available for Linux, Mac and Windows.&lt;/p&gt;
&lt;h3 id="2-create-your-project-structure"&gt;2. Create Your Project Structure&lt;/h3&gt;
&lt;p&gt;Create a folder structure for your migration project. At Another Cup of Coffee, we use a variation of following project structure.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Secrets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backups&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yml&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drupal&lt;/span&gt;&lt;span class="o"&gt;-[&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;]/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dockerfile&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phpmyadmin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dockerfile&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Drupal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;migration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;utilities&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="file-and-database-persistence"&gt;File and Database Persistence&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;File Changes: Drupal core/theme/module changes will persist on host through a mapping to the &lt;code&gt;./src&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Database Changes: the database will be stored in a named volume managed by Docker. The data will persist across container restarts/rebuilds unless the volume is manually deleted.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-download-the-drupal-installation-files"&gt;3. Download the Drupal Installation Files&lt;/h3&gt;
&lt;p&gt;We will use Drupal 7.103 for our container. (Note: &lt;em&gt;This is the newest version, released on 4 December 2024, as of the last update for this article.&lt;/em&gt;) &lt;a href="https://www.drupal.org/project/drupal/releases/7.103" target="_blank" rel="noopener noreferrer"&gt;Download it from the Drupal website&lt;/a&gt; and extract the files into your &lt;code&gt;./src&lt;/code&gt; directory. &lt;/p&gt;
&lt;p&gt;The container will create a &lt;a href="https://docs.docker.com/engine/storage/bind-mounts/" target="_blank" rel="noopener noreferrer"&gt;bind mount&lt;/a&gt; to map the &lt;code&gt;./src&lt;/code&gt; directory on the host machine to &lt;code&gt;/var/www/html&lt;/code&gt; inside the container. This means that any changes made to the files in the container at &lt;code&gt;/var/www/html&lt;/code&gt; will be reflected in the &lt;code&gt;./src&lt;/code&gt; directory on the host machine, and vice versa.&lt;/p&gt;
&lt;h3 id="4-create-your-env-file"&gt;4. Create Your &lt;code&gt;.env&lt;/code&gt; File&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;.env&lt;/code&gt; file is a text file used by Docker Compose to define environment variables. These variables can be referenced in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file to make your configurations more flexible and easier to manage. It's a good way of preventing sensitive information like passwords from being included in source control.&lt;/p&gt;
&lt;p&gt;It will look something like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;DRUPAL_DB_HOST=db
DRUPAL_DB_USER=dbuser
DRUPAL_DB_PASSWORD=dbpass
DRUPAL_DB_NAME=drupal_db

MYSQL_ROOT_PASSWORD=root
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ensure you adjust it to use your own variables.&lt;/p&gt;
&lt;h3 id="4-create-your-drupal-dockerfile"&gt;4. Create Your Drupal Dockerfile&lt;/h3&gt;
&lt;p&gt;The Dockerfile sets out the instructions for creating a Docker image, defining the base image, environment variables, software installations, and configuration commands.&lt;/p&gt;
&lt;p&gt;An image for Drupal 7.103 with Apache, Drush and PHP Composer might look like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drupal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;7.103&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apache&lt;/span&gt;

&lt;span class="c1"&gt;# Install required PHP extensions and tools&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;unzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;libicu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;libzip&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;libxml2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;libonig&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;zlib1g&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pdo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pdo_mysql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;intl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;

&lt;span class="c1"&gt;# Configure PHP&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"memory_limit = 256M"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ini&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"upload_max_filesize = 64M"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ini&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post_max_size = 64M"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ini&lt;/span&gt;

&lt;span class="c1"&gt;# Install PHP extensions needed by Drupal&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pdo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pdo_mysql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;intl&lt;/span&gt;

&lt;span class="c1"&gt;# Enable Apache mods commonly needed by Drupal&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a2enmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rewrite&lt;/span&gt;

&lt;span class="c1"&gt;# Install Composer (from the Composer official image)&lt;/span&gt;
&lt;span class="n"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;composer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;composer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;composer&lt;/span&gt;

&lt;span class="c1"&gt;# Install Drush globally via Composer&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;composer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drush&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;drush&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;8.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Ensure Drush is on PATH&lt;/span&gt;
&lt;span class="n"&gt;ENV&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/root/.composer/vendor/bin:${PATH}"&lt;/span&gt;

&lt;span class="c1"&gt;# Set the container working directory&lt;/span&gt;
&lt;span class="n"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;

&lt;span class="c1"&gt;# Ensure the Apache user (www-data) owns the web root&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This grabs the offical Docker Hub repository image for Drupal 7.103 with the Apache web server. You'll notice the Drupal image doesn't include any specifications for a database. This will be handled by the &lt;code&gt;docker-compose.yml&lt;/code&gt; file later.&lt;/p&gt;
&lt;p&gt;If you followed my project structure above, place this Dockerfile in a &lt;code&gt;drupal-7.103&lt;/code&gt; folder inside the &lt;code&gt;docker&lt;/code&gt; subdirectory.&lt;/p&gt;
&lt;p&gt;Adjust the Dockerfile for your own needs but here are some notes to avoid headaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;default-mysql-client&lt;/code&gt;: If you're familiar with installing MySQL on a Linux system, you might be tempted to run &lt;code&gt;apt-get&lt;/code&gt; for &lt;code&gt;mysql-client&lt;/code&gt;. Don't do this. The Docker repository for &lt;code&gt;drupal:7.103-apache&lt;/code&gt; is based on Debian Bookworm (Debian 12). Debian has stopped packaging &lt;code&gt;mysql-client&lt;/code&gt; as of Debian 10 so you will encounter errors. Make sure you use the &lt;code&gt;default-mysql-client&lt;/code&gt; metapackage instead. This will install the MySQL compatible &lt;code&gt;mariadb-client&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;composer:2&lt;/code&gt;: Although Drupal 7 does not have native Composer support, we are using it to install Drush.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drush:8&lt;/code&gt;: Install Drush 8, which is the latest compatible version Drupal 7 websites. A later version will install but you will encounter errors running some Drush commands.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-create-a-phpmyadmin-dockerfile-optional"&gt;5. Create a phpMyAdmin Dockerfile (optional)&lt;/h3&gt;
&lt;p&gt;This step is optional but it's useful to install phpMyAdmin so that you can do some basic database tasks. A phpMyAdmin Dockerfile that pulls the latest version of phpMyAdmin from the official Docker Hub repository would look like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;FROM phpmyadmin:latest

# Add ServerName configuration
RUN echo "ServerName localhost" &amp;gt;&amp;gt; /etc/apache2/apache2.conf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Place this Dockerfile in a &lt;code&gt;phpmyadmin&lt;/code&gt; folder inside the &lt;code&gt;docker&lt;/code&gt; subdirectory.&lt;/p&gt;
&lt;h3 id="6-create-your-docker-composeyml-file"&gt;6. Create Your &lt;code&gt;docker-compose.yml&lt;/code&gt; File&lt;/h3&gt;
&lt;p&gt;Docker Compose will let you define and run all the necessary applications using a &lt;code&gt;docker-compose.yml&lt;/code&gt; file. The example below defines services for Drupal, MySQL and phpMyAdmin, and should be saved in the root of your project folder. Please read the &lt;a href="https://docs.docker.com/reference/compose-file/" target="_blank" rel="noopener noreferrer"&gt;Compose file reference&lt;/a&gt; for a detailed explanation of each section in order to customise it for your needs.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;drupal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;dockerfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;drupal&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;7.103&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Dockerfile&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8088:80"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# Ensure you copy your Drupal site here to avoid permission issues&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# If you are starting with a fresh Drupal installation, download&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# the correct Drupal version and extract it here.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# https://www.drupal.org/project/drupal/releases/7.103&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_healthy&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;env_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_HOST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_HOST&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_USER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PHP_FPM_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PHP_FPM_GROUP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.7&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;env_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_USER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3307:3306"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"CMD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mysqladmin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ping"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-u"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drupal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-pdrupal_pass"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;phpmyadmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;dockerfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;phpmyadmin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Dockerfile&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8081:80"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;env_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PMA_HOST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_HOST&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PMA_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_USER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PMA_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DRUPAL_DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PMA_ABSOLUTE_URI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;db_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="the-mysql-image"&gt;The MySQL Image&lt;/h4&gt;
&lt;p&gt;You will see that I used MySQL 5.7 in this configuration to simulate an outdated environment. MySQL 5.7 reached its end of life in October 2023 but as of late-2024, we have clients who, for one reason or another, are still running MySQL 5.7. They are not alone. A September 2024 article from Percona, a database software and support company, has a FAQ section titled, "&lt;a href="https://www.percona.com/blog/mysql-8-0-vs-5-7-are-the-newer-versions-more-problematic/" target="_blank" rel="noopener noreferrer"&gt;Should I upgrade from MySQL 5.7 to 8.0?&lt;/a&gt;" Clearly, a large percentage of sites are still running legacy databases on their web stack.&lt;/p&gt;
&lt;p&gt;If you are using MySQL versions 8.x, visit Docker Hub's &lt;a href="https://hub.docker.com/_/mysql" target="_blank" rel="noopener noreferrer"&gt;MySQL repository&lt;/a&gt; to see the relevant image tags. You will need to update this section:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    db:
        image: mysql:5.7
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also, most development machines will already have a MySQL server running on the local port &lt;code&gt;3306&lt;/code&gt;. To avoid conflicts, this &lt;code&gt;docker-compose.yml&lt;/code&gt; maps the container's MySQL port to the local port &lt;code&gt;3307&lt;/code&gt;. Adjust this to suit your own setup.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;        ports:
            - "3307:3306"
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="7-build-and-start-the-environment"&gt;7. Build and Start the Environment&lt;/h3&gt;
&lt;p&gt;Now you're ready to build and start your environment. Open up a terminal and run the following command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;docker compose up -d --build
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will use Docker Compose to build the containers and run them in detached mode. It may take a few minutes to complete if you're running this command for the first time. Docker will need to download all the images from the Docker repository before building.&lt;/p&gt;
&lt;h3 id="8-access-your-local-drupal-setup"&gt;8. Access Your Local Drupal Setup&lt;/h3&gt;
&lt;p&gt;If all goes well you should have your environment ready to install Drupal 7. Visit &lt;code&gt;http://localhost:8080&lt;/code&gt; in your browser to access the Drupal 7 instance running in the Docker container.&lt;/p&gt;
&lt;p&gt;Follow the installation wizard and 
Use these database settings in your .env file.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span id="testing-workflow"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="6-testing-and-workflow"&gt;6. Testing and Workflow&lt;/h2&gt;
&lt;p&gt;You can use these commands to test your installation. Check the documentation in the &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/#additional-resources"&gt;Additional Resources&lt;/a&gt; section below for more commands.&lt;/p&gt;
&lt;h3 id="basic-testing"&gt;Basic Testing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Container Status:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Check if all containers are running&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;ps
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Expected output: db, drupal and phpMyAdmin services should show "Up" status&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Drupal Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access the Drupal site at http://localhost:8088&lt;/li&gt;
&lt;li&gt;You should see the Drupal installation page or site&lt;/li&gt;
&lt;li&gt;Check error logs if needed:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;drupal
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Drush Functionality:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Test Drush status&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;status

&lt;span class="c1"&gt;# Test Drush version&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;--version

&lt;span class="c1"&gt;# Test site-specific Drush commands&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;core-status
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Composer Functionality:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Test Composer version&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;composer&lt;span class="w"&gt; &lt;/span&gt;--version

&lt;span class="c1"&gt;# Test Composer diagnostics&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;composer&lt;span class="w"&gt; &lt;/span&gt;diagnose
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Database Connection:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Test database connection via Drush&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;sql-connect

&lt;span class="c1"&gt;# Test phpMyAdmin access&lt;/span&gt;
&lt;span class="c1"&gt;# Visit http://localhost:8081&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="testing-your-migration-utilities-with-sample-content"&gt;Testing Your Migration Utilities With Sample Content&lt;/h3&gt;
&lt;p&gt;You can use Drush to generate sample content. This can be useful for testing your migration utilities.&lt;/p&gt;
&lt;h4 id="1-log-into-the-containers-terminal"&gt;1. Log Into the Container's Terminal&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="2-install-helper-modules"&gt;2. Install Helper Modules&lt;/h4&gt;
&lt;p&gt;Install the &lt;code&gt;devel&lt;/code&gt;, &lt;code&gt;devel_generate&lt;/code&gt;, and &lt;code&gt;taxonomy_manager&lt;/code&gt; modules:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;drush&lt;span class="w"&gt; &lt;/span&gt;en&lt;span class="w"&gt; &lt;/span&gt;devel&lt;span class="w"&gt; &lt;/span&gt;-y
drush&lt;span class="w"&gt; &lt;/span&gt;en&lt;span class="w"&gt; &lt;/span&gt;devel_generate&lt;span class="w"&gt; &lt;/span&gt;-y
drush&lt;span class="w"&gt; &lt;/span&gt;en&lt;span class="w"&gt; &lt;/span&gt;taxonomy_manager&lt;span class="w"&gt; &lt;/span&gt;-y
&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Devel Module:&lt;/strong&gt; The Devel module is a comprehensive toolkit for Drupal developers that offers several submodules and features to aid in development and debugging.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Devel Generate Module:&lt;/strong&gt; The Devel Generate module is a submodule of the Devel module, specifically designed for generating dummy content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Taxonomy Manager Module:&lt;/strong&gt; Use the Taxonomy Manager module to mass insert taxonomy terms. &lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3-generate-sample-content"&gt;3. Generate Sample Content&lt;/h4&gt;
&lt;p&gt;Now generate some sample content:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Generate nodes with randon content&lt;/span&gt;
drush&lt;span class="w"&gt; &lt;/span&gt;generate-content&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;

&lt;span class="c1"&gt;# Generate users&lt;/span&gt;
drush&lt;span class="w"&gt; &lt;/span&gt;generate-users&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;

&lt;span class="c1"&gt;# Generate taxonomy terms&lt;/span&gt;
drush&lt;span class="w"&gt; &lt;/span&gt;generate-terms&lt;span class="w"&gt; &lt;/span&gt;tags&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="c1"&gt;# Generate nodes for a specific content type&lt;/span&gt;
drush&lt;span class="w"&gt; &lt;/span&gt;generate-content&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--types&lt;span class="o"&gt;=&lt;/span&gt;article

&lt;span class="c1"&gt;# Generate comments on random nodes:&lt;/span&gt;
drush&lt;span class="w"&gt; &lt;/span&gt;generate-comments&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="development-workflow"&gt;Development Workflow&lt;/h3&gt;
&lt;p&gt;Here are some basic commands to support your development and migration workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starting/Stopping Environment:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Start containers&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;up&lt;span class="w"&gt; &lt;/span&gt;-d

&lt;span class="c1"&gt;# Stop containers&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;down

&lt;span class="c1"&gt;# View logs&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;-f
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Using Drush:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Access Drush&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush

&lt;span class="c1"&gt;# Clear cache&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;cc&lt;span class="w"&gt; &lt;/span&gt;all

&lt;span class="c1"&gt;# Update database&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;drush&lt;span class="w"&gt; &lt;/span&gt;updb
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Working with Composer:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Install dependencies&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;composer&lt;span class="w"&gt; &lt;/span&gt;install

&lt;span class="c1"&gt;# Add new package&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;composer&lt;span class="w"&gt; &lt;/span&gt;require&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;package-name&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Service Access:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drupal: http://localhost:8088&lt;ul&gt;
&lt;li&gt;Username and password as set during the installation process.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;phpMyAdmin: http://localhost:8081&lt;ul&gt;
&lt;li&gt;Username: (from .env)&lt;/li&gt;
&lt;li&gt;Password: (from .env)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MySQL: localhost:3307&lt;ul&gt;
&lt;li&gt;Username: (from .env)&lt;/li&gt;
&lt;li&gt;Password: (from .env)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Database Operations:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Export database&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;db&lt;span class="w"&gt; &lt;/span&gt;mysqldump&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;user&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="o"&gt;[&lt;/span&gt;root-password&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;backup.sql

&lt;span class="c1"&gt;# Import database&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-T&lt;span class="w"&gt; &lt;/span&gt;db&lt;span class="w"&gt; &lt;/span&gt;mysql&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;user&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="o"&gt;[&lt;/span&gt;root-password&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;drupal&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;backup.sql
&lt;/pre&gt;&lt;/div&gt;

&lt;hr&gt;
&lt;p&gt;&lt;span id="preparing-migration"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="7-preparing-for-your-drupal-7-migration"&gt;7. Preparing for Your Drupal 7 Migration&lt;/h2&gt;
&lt;p&gt;If you made it this far, you will have a fully working and tested Docker container with Drupal 7, access to the database, and some basic tools to support migration or upgrade tasks. This environment isolates your development work, ensuring that your live site remains unaffected throughout the process.&lt;/p&gt;
&lt;h3 id="importing-your-live-drupal-installation-into-the-container"&gt;Importing Your Live Drupal Installation into the Container&lt;/h3&gt;
&lt;p&gt;To get started with your migration project, you'll need to transfer your existing live Drupal installation into the container. The Drupal 7 documentation for &lt;a href="https://www.drupal.org/docs/7/backing-up-and-migrating-a-site/migrating-a-site" target="_blank" rel="noopener noreferrer"&gt;migrating a site between environments&lt;/a&gt; will give you an outline of the steps.&lt;/p&gt;
&lt;p&gt;As a summary, you begin by exporting the database and files from your live site. Once you have these, you can load the database into the container's MySQL instance and place the site's files in the &lt;code&gt;./src&lt;/code&gt; directory on your host filesystem. (Remember, we created a bind mount to map the &lt;code&gt;./src&lt;/code&gt; directory to the container's web directory at &lt;code&gt;/var/www/html&lt;/code&gt;.) This gives you a working copy of your live site, but in a safe, sandboxed environment. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; &lt;em&gt;In this article we used Drupal 7.103 for our container. To avoid problems with version conflicts, you should update your live Drupal site to the same version before exporting.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="creating-a-pristine-snapshot"&gt;Creating a Pristine Snapshot&lt;/h3&gt;
&lt;p&gt;Before diving into migration tasks, it's a good idea to create a snapshot of the container in its pristine state. This serves as a clean starting point you can revert to if anything goes wrong during the migration process. A snapshot ensures you won't need to repeat the setup steps from scratch if you need to restart or experiment with different migration approaches.&lt;/p&gt;
&lt;p&gt;You create a snapshot by committing the current state of your container to a new Docker image. I'll cover this in a separate article but at this stage you will now be ready to use whatever tools you need to upgrade or migrate your Drupal 7 installation.&lt;/p&gt;
&lt;h3 id="migration-options"&gt;Migration Options&lt;/h3&gt;
&lt;p&gt;Instructions for the migration itself will need to be covered elsewhere as it's a very detailed process. However, you can check these resources for your migration options:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Upgrading to a newer version of Drupal&lt;/strong&gt;: You can use your container to trial-run the upgrade steps in the Drupal documentation, "&lt;a href="https://www.drupal.org/docs/upgrading-drupal/upgrading-from-drupal-6-or-drupal-7" target="_blank" rel="noopener noreferrer"&gt;Upgrading from Drupal 6 or Drupal 7&lt;/a&gt;."&lt;/li&gt;

    &lt;li&gt;&lt;strong&gt;Migrating to another platform:&lt;/strong&gt; If you plan to migrate to another platform such as WordPress, please see our &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" title="Drupal to WordPress Migration Guide"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;hr&gt;
&lt;p&gt;&lt;span id="conclusion"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="8-conclusion"&gt;8. Conclusion&lt;/h2&gt;
&lt;p&gt;Docker provides a very practical and reliable way to set up migration environments. While it requires an initial investment in learning, the long-term benefits include consistent workflows, reliable environments, and over-all, fewer frustrations when getting started with a migration project. Docker won't solve every migration challenge, but in my experience, it definitely simplifies many of the technical hurdles involved. &lt;/p&gt;
&lt;p&gt;If you're facing challenges with Drupal 7 migrations or need expert assistance, &lt;a href="https://anothercoffee.net/contact/"&gt;reach out to us&lt;/a&gt; at Another Cup of Coffee. We can help make your next project smooth, efficient, and future-proof.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span id="additional-resources"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="additional-resources"&gt;Additional Resources&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.drupal.org/docs" target="_blank" rel="noopener noreferrer"&gt;Official Drupal Documentation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://docs.docker.com/" target="_blank" rel="noopener noreferrer"&gt;Docker Documentation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.drush.org/latest/" target="_blank" rel="noopener noreferrer"&gt;Drush Documentation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://getcomposer.org/doc/%20target=" _blank" rel="noopener noreferrer"&gt;Composer Documentation&lt;/a&gt;&lt;/li&gt;            
&lt;/ul&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h3&gt;You may also like&lt;/h3&gt;

    &lt;div class="row"&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                        &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h4&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option-300x150.jpg" class="card-img-top" alt="Drupal 7 End of Life: Why WordPress is the Best Migration Option for Lower Maintenance Sites"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-end-of-life-why-wordpress-is-the-best-migration-option/" class="listtitle"&gt;Drupal 7 End of Life: Why WordPress is the Best Migration Option for Lower Maintenance Sites&lt;/a&gt;&lt;/h4&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-12-17T14:25:15Z" title="17 December 2024"&gt;17 December 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Drupal 7 support ends January 2025. Discover why WordPress is the cost-effective, user-friendly CMS for small agencies, freelancers, and businesses.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;!--
      &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
        &lt;div class="card h-100"&gt;
              &lt;a href="/still-alive-a-micro-agencys-20-year-journey/"&gt;
                      &lt;img src="/images/a-west-london-micro-agencys-journey-to-ai-featured.jpg" class="card-img-top" alt="Still Alive: A Micro Agency's 20 Year Journey"&gt;&lt;/a&gt;
              &lt;div class="card-body d-flex flex-column"&gt;
                  &lt;h3 class="card-title"&gt;&lt;a href="/still-alive-a-micro-agencys-20-year-journey/" class="listtitle"&gt;Still Alive: A Micro Agency's 20 Year Journey&lt;/a&gt;&lt;/h3&gt;
                  &lt;div class="mb-2"&gt;
                      &lt;span&gt;&lt;time class="listdate" datetime="2024-10-15T15:28:15Z" title="15 October 2024"&gt;15 October 2024&lt;/time&gt;&lt;/span&gt;
                  &lt;/div&gt;

                      &lt;p class="card-text flex-grow-1"&gt;This article will be the first in a series where I'll share how Artificial Intelligence has reshaped how we operate at Another Cup of Coffee.&lt;/p&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    --&gt;
            &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
          &lt;div class="card h-100"&gt;
              &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                      &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
              &lt;div class="card-body d-flex flex-column"&gt;
                  &lt;h4 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h4&gt;
                  &lt;div class="mb-2"&gt;
                      &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                  &lt;/div&gt;

                      &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

&lt;/section&gt;</description><category>Docker</category><category>Drupal</category><category>End of Life</category><category>Migration</category><guid>https://anothercoffee.net/drupal-7-docker-containers-migration-projects/</guid><pubDate>Mon, 09 Sep 2024 13:25:15 GMT</pubDate></item><item><title>Fixing Access Forbidden (403) issues after migrating WordPress to a static site</title><link>https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;After migrating a WordPress site to static files, Google Search Console may start sending you notifications about page indexing problems. Often the issue will be marked as ‘&lt;em&gt;Blocked due to access forbidden (403)&lt;/em&gt;‘, a fairly common error that can have different causes, depending on how your website and server is set up.&lt;/p&gt;


&lt;figure class="figure d-flex flex-column align-items-center"&gt;
    &lt;img src="https://anothercoffee.net/images/no-entry-Lucian-Alexe-Unsplash-l0w1ftNPZ9s.jpg" alt="No entry road stock photo - Access Forbidden 403 error" class="figure-img img-fluid rounded" width="800" height="417"&gt;
&lt;/figure&gt;

&lt;p&gt;This article specifically deals with the case when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You previously had a working WordPress installation;&lt;/li&gt;
&lt;li&gt;You migrate the site to static HTML files on an Apache web server;&lt;/li&gt;
&lt;li&gt;Google Search Console starts complaining about page indexing problems.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If this does not apply to your situation, the proposed solution may not work but you will still find the explanation useful in diagnosing the problem.&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;div class="toc"&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#forbidden-403"&gt;The Forbidden 403 error&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#wordpress-requests"&gt;How WordPress serves your browser requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#static-files"&gt;Serving static files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#directory-index"&gt;Web server directory index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#generating-static-site"&gt;Generating static HTML files from a WordPress site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#why-forbidden"&gt;Why access is forbidden&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/#how-to-fix"&gt;How to fix the Access Forbidden (403) error&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

&lt;h2 id="forbidden-403"&gt;The Forbidden 403 error&lt;/h2&gt;
&lt;p&gt;Being in the 4XX category of HTTP response codes, &lt;em&gt;Access Forbidden (403)&lt;/em&gt; is a client-side error that may show in a message similar to the following combinations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Access Forbidden (403)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;403 Forbidden&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;HTTP 403&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;HTTP Error 403 – Forbidden&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Forbidden: You don’t have permission to access [directory path] on this server&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Google Search Console notification email will be about page indexing problems with  ‘&lt;em&gt;Blocked due to access forbidden (403)&lt;/em&gt;‘ as one of the Top Issues.&lt;/p&gt;

&lt;figure class="figure d-flex flex-column align-items-center"&gt;
    &lt;img src="https://anothercoffee.net/images/anothercoffee-google-search-console-page-indexing-issues.png" alt="Google Search Console Page indexing issues" class="figure-img img-fluid rounded" width="498" height="348"&gt;
    &lt;figcaption&gt;A typical Access Forbidden (403) notification from the Google Search Console&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Nevertheless, it being a client-side error doesn’t necessary isolate the problem to your browser. Many solutions online will advise you to clear your cache or refresh the page. This is easy to do and usually a sensible first step but if you found the issue via a Google Search Console notification after a WordPress migration, these recommendations won’t help.&lt;/p&gt;
&lt;p&gt;The 403 response code essentially means that a web client, such as your browser, does not have permission to access the requested resource. The web server understands the request but can’t allow access due to file permissions settings or a server misconfiguration. When it comes to WordPress, the error will almost certainly be due to a misbehaving plugin, a &lt;a href="https://kinsta.com/blog/403-forbidden-error/" target="_blank" rel="noreferrer noopener nofollow"&gt;corrupt .htaccess file or incorrect file permissions&lt;/a&gt;. However, if you’ve recently converted your WordPress installation to a static site, the error will most likely have a different cause.&lt;/p&gt;
&lt;p&gt;To understand why the &lt;em&gt;Access Forbidden&lt;/em&gt; error happens, it’s worth reviewing the differences between how the WordPress content management system (CMS) serves content and how a static site responds to web client requests.&lt;/p&gt;

&lt;h2 id="wordpress-requests"&gt;How WordPress serves your browser requests&lt;/h2&gt;
&lt;p&gt;WordPress is a database-driven Content management system (CMS). Most of the content is stored in a database and URLs generally do not correspond to any files in the web server’s filesystem. Instead, URLs are external references to the database content. WordPress calls these references &lt;em&gt;permalinks&lt;/em&gt; and its rewriting engine uses internal &lt;a href="https://codex.wordpress.org/Using_Permalinks"&gt;rules&lt;/a&gt;, specified in the &lt;a href="https://codex.wordpress.org/Using_Permalinks#Choosing_your_permalink_structure"&gt;permalink settings&lt;/a&gt;, to build permalinks dynamically. When a client requests a page from the site, WordPress takes care of serving the correct content. Included with the pages will be any XML-based RSS (Really Simple Syndication) feeds. WordPress will send the HTML page or an XML feed, depending on what the client requests.&lt;/p&gt;
&lt;p&gt;For example, if you set the permalink settings to the &lt;em&gt;Post name&lt;/em&gt; structure, WordPress will generate a HTML post when you browse to the following URL:&lt;br&gt;&lt;code&gt;https://example.com/your-post/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The RSS feed for that post will be at:&lt;br&gt;&lt;code&gt;https://example.com/your-post/feed/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;RSS feeds are normally parsed by an RSS client rather than for displaying directly in the browser window so requesting the RSS feed resource will not send HTML. Instead WordPress will generate XML for the feed.&lt;/p&gt;

&lt;h2 id="static-files"&gt;Serving static files&lt;/h2&gt;
&lt;p&gt;By definition, static sites have no way of dynamically generating content based on the browser request. You make a request to the web server at a given URL and if the resource is present with the correct access permissions, the web server will go ahead and serve the file. The URL for a static site will normally look something like:&lt;br&gt;&lt;code&gt;https://example.com/your-post.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have an RSS feed, it might look something like:&lt;br&gt;&lt;code&gt;https://example.com/feed.xml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice that the request includes the path to the file, such as &lt;code&gt;your-post.html&lt;/code&gt; or &lt;code&gt;feed.xml&lt;/code&gt;. The file is not present in a WordPress permalink because, as mentioned previously, it is only a reference to the actual content stored in the database.&lt;/p&gt;

&lt;h2 id="directory-index"&gt;Web server directory index&lt;/h2&gt;
&lt;p&gt;Web servers such as Apache also have a &lt;a href="https://httpd.apache.org/docs/2.4/mod/mod_dir.html#directoryindex" target="_blank" rel="noreferrer noopener nofollow"&gt;Directory Index directive&lt;/a&gt;. This is a configuration that can set the server to automatically send a file when a client makes a request without a filename in the URL. The file known as the directory index and is normally named &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Web servers such as Apache also have a &lt;a href="https://httpd.apache.org/docs/2.4/mod/mod_dir.html#directoryindex" target="_blank" rel="noreferrer noopener nofollow"&gt;Directory Index directive&lt;/a&gt;. In the early days of the web, you could browse to a folder in the web server filesystem and get a listing of all the files present. For security, most web hosts now disable this feature for most of their hosting services. The Directory Index directive is a configuration that can set the server to automatically send a file when a client request only includes the folder name in the URL. For most hosting services, the standard directory index files are normally &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;index.php&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Say a client makes a request for the following:&lt;br&gt;&lt;code&gt;https://example.com/docs/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If the directory index is set to &lt;code&gt;index.html&lt;/code&gt;, the server would return:&lt;br&gt;&lt;code&gt;http://example.com/docs/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;index.html&lt;/code&gt; renders as a web page in the browser. Directory index resources can also be set to other file types like &lt;code&gt;index.txt&lt;/code&gt; or &lt;code&gt;index.xml&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="generating-static-site"&gt;Generating static HTML files from a WordPress site&lt;/h2&gt;
&lt;p&gt;WordPress plugins such as &lt;a href="https://wordpress.org/plugins/simply-static/" target="_blank" rel="noreferrer noopener nofollow"&gt;Simply Static&lt;/a&gt; will crawl your site to generate static HTML file copies for the pages. Since WordPress includes RSS feeds, static XML copies will also be generated for these feeds. The tables below show typical WordPress permalinks and their equivalents after static HTML copies are generated.&lt;/p&gt;
&lt;figure class="wp-block-table"&gt;
&lt;table class="has-fixed-layout"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="width:30%"&gt;Page type&lt;/th&gt;
&lt;th&gt;WordPress permalink&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page content&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;code&gt;https://example.com/your-post/&lt;/code&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSS feed&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;code&gt;https://example.com/your-post/feed/&lt;/code&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;
&lt;figure class="wp-block-table"&gt;
&lt;table class="has-fixed-layout"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="width:30%"&gt;Page type&lt;/th&gt;
&lt;th&gt;Static file URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page content&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;code&gt;https://example.com/your-post/index.html&lt;/code&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSS feed&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;code&gt;https://example.com/your-post/feed/index.xml&lt;/code&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;
&lt;p&gt;Once again notice that the static site specifies the filename in the path whereas the WordPress permalink, when set to the &lt;em&gt;Post name&lt;/em&gt; structure, does not.&lt;/p&gt;
&lt;h2 id="why-forbidden"&gt;Why access is forbidden&lt;/h2&gt;
&lt;p&gt;We have all the puzzle pieces to understand why you would get the &lt;em&gt;Access Forbidden (403)&lt;/em&gt; error and how you can fix the problem. After you migrate your WordPress site to static files, the old permalink paths to pages will still serve a web page because most hosting providers have &lt;code&gt;index.html&lt;/code&gt; as a directory index resource.&lt;/p&gt;
&lt;p&gt;You can request the URL in the WordPress post name permalink format:&lt;br&gt;&lt;code&gt;https://example.com/your-post/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The static file generator would have created an index.html in this location:&lt;br&gt;&lt;code&gt;https://example.com/your-post/&lt;/code&gt;index.html&lt;/p&gt;
&lt;p&gt;The webserver sees the &lt;code&gt;index.html&lt;/code&gt; in the filesystem and delivers it to the browser which can render the web page content. Human site visitors will be perfectly happy because they receive the web page resources they expect. However, Googlebot, Google’s web page crawler, will spider through your site &lt;em&gt;including the RSS feed locations&lt;/em&gt;. Remember, the RSS feed folders will contain an &lt;code&gt;index.xml&lt;/code&gt;. XML files are not normally a default directory index resource for most web hosts. Since there is no &lt;code&gt;index.html&lt;/code&gt; file in the feed folder, the web server thinks it’s being asked to deliver a file listing. Again remember that file listings are disabled by most web hosts for security. Thus you get the error:&lt;/p&gt;
&lt;p class="has-text-align-center"&gt;&lt;code&gt;Access Forbidden (403)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You are forbidden by the web server to access that folder listing.&lt;/p&gt;

&lt;h2 id="how-to-fix"&gt;How to fix the Access Forbidden (403) error&lt;/h2&gt;
&lt;p&gt;Now that we know exactly why we get this error we attempt a fix. If your host runs Apache, the solution will likely be simple. Edit or create the &lt;code&gt;.htaccess&lt;/code&gt; file in the root of your site and add &lt;code&gt;index.xml&lt;/code&gt; to the list of directory index resources. For example, if it not already there, add the following line somewhere near the top of the file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DirectoryIndex index.html index.php index.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;index.xml&lt;/code&gt; in the list will tell Apache to serve the XML file when Googlebot requests the RSS feed directory. While you’re doing this, inspect a few o the feed directories to make sure the &lt;code&gt;index.xml&lt;/code&gt; files have the correct permissions (usually &lt;code&gt;775&lt;/code&gt; for most server setups) and the correct ownership. The ownership settings should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;user&lt;/code&gt;&lt;/strong&gt; is the user account with root privileges on your web server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;group&lt;/code&gt;&lt;/strong&gt; is usually &lt;strong&gt;&lt;code&gt;www-data&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;apache&lt;/code&gt;&lt;/strong&gt; but you may need to check this with your hosting provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can test your changes by pointing your browser to a few feed directories to see if the server returns XML. Remember to leave out the &lt;code&gt;index.xml&lt;/code&gt; file and specify the directory only. For example:&lt;br&gt;&lt;code&gt;https://example.com/your-post/feed/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your browser displays the RSS feed XML, you know the problem has been solved and can be reasonably confident that Googlebot won’t encounter the 403 error.&lt;/p&gt;
&lt;p&gt;This may not work if you’re running a VPS server for your site where some more in-depth configuration may be needed, such as editing your Apache configuration files. Also, &lt;code&gt;.htaccess&lt;/code&gt; is not used by other web servers such as NGINX. In these cases, your next port of call should be to contact your hosting provider. Tell them that you would like to add &lt;code&gt;index.xml&lt;/code&gt; to the directory index resources so that the XML file is served when a visitor lands in a directory. Most good hosting companies will be able to guide you on your options or make the change on your behalf. After you’ve applied the fix, be sure to go to your Google Search Console to revalidate the affected page.&lt;/p&gt;

&lt;div class="footnotes"&gt;
    &lt;p&gt;Photo by &lt;a href="https://unsplash.com/@lucian_alexe?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" target="_blank" rel="nofollow"&gt;Lucian Alexe&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/l0w1ftNPZ9s?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" target="_blank" rel="nofollow"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h2&gt;You may also like&lt;/h2&gt;

    &lt;div class="row"&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                        &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                        &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/section&gt;</description><category>Apache</category><category>Blog</category><category>Help</category><category>Migration</category><category>SEO</category><category>Sys Admin</category><category>Troubleshooting</category><category>WordPress</category><guid>https://anothercoffee.net/fixing-access-forbidden-403-issues-after-migrating-wordpress-to-a-static-site/</guid><pubDate>Thu, 09 Feb 2023 01:15:57 GMT</pubDate></item><item><title>How to set up a MySQL database server on Ubuntu for Drupal to WordPress migrations</title><link>https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;Content Management System (CMS) migration projects involve moving data between databases with different schemas. Table names, field types and constraints often don’t match up, leading to a number of &lt;a href="https://anothercoffee.net/tag/troubleshooting/"&gt;frustrating errors&lt;/a&gt;. This is especially the case with &lt;a href="https://anothercoffee.net/content-migration-process/"&gt;highly complex Drupal to WordPress migrations&lt;/a&gt; that use custom scripts to extract, transform and load the data. These projects can seem like you’re constantly hitting roadblocks throughout. I’ve found from experience that running the migration in an appropriate development environment can reduce a great deal of effort. In this guide, I will describe how to set up a MySQL database server on Ubuntu for Drupal to WordPress migrations.&lt;/p&gt;

&lt;figure class="figure d-flex flex-column align-items-center"&gt;
    &lt;img src="https://anothercoffee.net/images/rubaitul-azad-Y9kOsyoWyaU-unsplash.jpg" alt="MySQL database logo" class="figure-img img-fluid rounded" width="800" height="500"&gt;
&lt;/figure&gt;

&lt;h2 id="drupal-to-wordPress-mysql-server-toc"&gt;Table of contents&lt;/h2&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#why-mysql-ubuntu"&gt;Why MySQL and Ubuntu?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#installing-mysql-for-migrations"&gt;Installing MySQL on Ubuntu for CMS migrations&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#setup-lamp-migration-platform"&gt;Set up your Ubuntu LAMP migration platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#install-mysql"&gt;Install MySQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#setting-sql-mode"&gt;Setting the MySQL server SQL mode&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#set-global-sql-mode"&gt;Option 1: Setting the global sql_mode in the database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#set-mysql-config-file"&gt;Option 2: setting the sql_mode in the MySQL configuration file&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#locating-mysql-conf"&gt;Locating the MySQL configuration file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#edit-mysql-config-file"&gt;Editing the MySQL configuration file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#potential-errors"&gt;Potential errors&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#mysqldump-access-denied-when-trying-to-dump-tablespaces"&gt;mysqldump access denied when trying to dump tablespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#ERROR-1067-42000-Invalid-default-value"&gt;ERROR 1067 (42000) Invalid default value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#Expression-1-of-SELECT-list-is-not-in-GROUP-BY-clause"&gt;Expression #1 of SELECT list is not in GROUP BY clause&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#Error-Code-2013-Lost-connection-to-MySQL-server"&gt;Error Code: 2013. Lost connection to MySQL server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;h2 id="why-mysql-ubuntu"&gt;Why MySQL and Ubuntu?&lt;/h2&gt;
&lt;p&gt;The first thing to address is &lt;em&gt;why MySQL and Ubuntu?&lt;/em&gt; How about MariaDB? How about Arch Linux, Mac OS or Windows? Yes! Any platform that runs Drupal and WordPress will work for your migration environment so you can use whatever you prefer. Personally, I have a fondness for &lt;a href="https://www.openbsd.org/" target="_blank" rel="noreferrer noopener"&gt;OpenBSD&lt;/a&gt; but it’s not a practical platform for a CMS migration. OpenBSD’s niche user-base means you’ll spend much longer installing necessary tools and troubleshooting errors.&lt;/p&gt;
&lt;p&gt;There are all sorts of tutorials covering MySQL on Ubuntu. This means you’re more likely to quickly find a solution from a web search when you hit a problem. Furthermore, if you need a software utility or program to help you get the job done, it will probably be available through &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dpkg&lt;/code&gt;,  &lt;code&gt;snap&lt;/code&gt; or &lt;code&gt;tasksel&lt;/code&gt;. Use whatever you like but for now, MySQL on Ubuntu is my recommended platform for Drupal to WordPress migrations. I expect this will be the case for some time to come. These projects are complex and time-consuming enough without making the &lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#mariadb-mysql-purge"&gt;job more difficult&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="installing-mysql-for-migrations"&gt;Installing MySQL on Ubuntu for CMS migrations&lt;/h2&gt;
&lt;p&gt;There are many detailed tutorials for installing MySQL on Ubuntu. DigitalOcean’s &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04" target="_blank" rel="noreferrer noopener nofollow"&gt;How To Install MySQL on Ubuntu 20.04&lt;/a&gt; is a good one and writing another won’t add much value. My guide will therefore only give a brief overview of the MySQL server installation steps. Instead, I will focus on the configuration areas specifically related avoiding problems on a CMS migration project.&lt;/p&gt;
&lt;p&gt;You may wonder why the migration environment should be much different from a live server. Migration projects require you to do things that aren’t supported by the CMS platform. You’re therefore likely to encounter &lt;a href="https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/#potential-errors"&gt;weird errors&lt;/a&gt; that aren’t normally found when running standard Drupal or WordPress.&lt;/p&gt;

&lt;h3 id="setup-lamp-migration-platform"&gt;Set up your Ubuntu LAMP migration platform&lt;/h3&gt;
&lt;p&gt;The main source of unusual errors is almost certainly because you’re migrating on a setup suited to a live website. Live server configurations are more restrictive than you need for a migration project. You can therefore save yourself a huge headache by rolling your own local migration environment. It might take a little longer to get started but you’ll save time by avoiding lots of unnecessary troubleshooting.&lt;/p&gt;
&lt;p&gt;I must highlight that this will be a &lt;em&gt;local migration environment&lt;/em&gt; and should not be accessible from the public internet. The normal security considerations with running a live content management system don’t apply when you’re working locally. By all means follow basic security measures mentioned the various tutorials for setting up Ubuntu and MySQL. Nevertheless, a highly secure setup is counterproductive for these projects and you can avoid trouble by being a little more permissive.&lt;/p&gt;
&lt;p&gt;Go ahead and &lt;a href="https://ubuntu.com/tutorials/install-ubuntu-desktop" target="_blank" rel="noreferrer noopener"&gt;install Ubuntu Desktop&lt;/a&gt;. Since this will be a development environment, you’ll want the Desktop environment rather than the more lightweight server version. Of course, you’ll still need to install a web and database server. &lt;a href="https://upcloud.com/community/tutorials/installing-lamp-stack-ubuntu/" target="_blank" rel="noreferrer noopener"&gt;Follow these instructions for installing LAMP stack&lt;/a&gt; but skip the step of installing MariaDB. As mentioned above, we’ll be using MySQL.&lt;/p&gt;


&lt;div id="mariadb-mysql-purge" class="alert alert-warning"&gt;
&lt;p class="has-text-align-left"&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; Installing MariaDB over MySQL or vice versa on Ubuntu 20.04 may lead to all sorts of &lt;a href="https://askubuntu.com/questions/1300304/mysql-not-working-after-uninstall-mariadb-in-ubuntu" target="_blank" rel="noreferrer noopener"&gt;problems starting up the database server&lt;/a&gt; with errors like the following:&lt;/p&gt;
&lt;p class="has-text-align-left"&gt;&lt;code&gt;Failed to start mysqld.service: Unit mysqld.service not found&lt;/code&gt;.&lt;/p&gt;
&lt;p class="has-text-align-left"&gt;The last time I did this, none of solutions mentioned online for purging the installation worked. I spent most of a day trying to fix the problem. In the end, I realised it was quicker to start again and rebuild the machine from scratch. This is a big reason why I decided to stick with MySQL as standard for my projects.&lt;/p&gt;
&lt;/div&gt;

&lt;h3 id="install-mysql"&gt;Install MySQL&lt;/h3&gt;
&lt;p&gt;You can read a more detailed tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04" target="_blank" rel="noreferrer noopener"&gt;installing MySQL on Ubuntu&lt;/a&gt; but here’s an overview.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the package index on your server: &lt;code&gt;sudo apt update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install MySQL server: &lt;code&gt;sudo apt install mysql-server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Secure MySQL: &lt;code&gt;sudo /usr/bin/mysql_secure_installation&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;mysql_secure_installation&lt;/code&gt; script doesn’t cause problems for migrations so it’s worth running.&lt;/p&gt;
&lt;p&gt;Create an admin user:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE USER ‘user’@'localhost’ IDENTIFIED BY ‘userpassword’;
GRANT ALL PRIVILEGES ON *.* to ‘user’@'localhost’ WITH GRANT OPTION;
FLUSH PRIVILEGES;
quit;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now create a user and database for your migration project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE USER ‘projectuser’@'localhost’ IDENTIFIED BY ‘password’;

CREATE DATABASE project_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
GRANT ALL ON project_db.* TO ‘projectuser’@'localhost’;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can give this user more restrictive permissions but &lt;code&gt;GRANT ALL&lt;/code&gt; avoids errors when running scripts and SQL queries on the database. Only the migration team should have access and you won’t need it after the migration so why not make your life easier? &lt;/p&gt;

&lt;h2 id="setting-sql-mode"&gt;Setting the MySQL server SQL mode&lt;/h2&gt;
&lt;p&gt;It’s possible that you won’t be familiar with &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html" target="_blank" rel="noreferrer noopener"&gt;SQL modes&lt;/a&gt; unless you’ve done some database administration work. For our purposes, SQL modes do two things:&lt;/p&gt;


&lt;ul&gt;
&lt;li&gt;change the types of queries you can run on your MySQL server;&lt;/li&gt;
&lt;li&gt;change the validation checks when altering the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve found that specific SQL modes need to be set for Drupal to WordPress projects. You may find that all sorts of strange errors appear if the correct modes aren’t set. The following sections show you two ways to set your MySQL server’s SQL mode.&lt;/p&gt;

&lt;h3 id="set-global-sql-mode"&gt;Option 1: Setting the global sql_mode in the database&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Login in to database as an admin user.&lt;/li&gt;
&lt;li&gt;View the current sql-modes using &lt;code&gt;SELECT @@GLOBAL.sql_mode;&lt;/code&gt; and make a copy if necessary.&lt;/li&gt;
&lt;li&gt;Copy the current modes (add or delete modes as needed) and paste in next step.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;ALLOW_INVALID_DATES&lt;/code&gt; and removes both &lt;code&gt;NO_ZERO_DATE, NO_ZERO_IN_DATE&lt;/code&gt; by setting the sql-modes with&lt;br&gt;&lt;code&gt;SET GLOBAL sql_mode = ‘STRICT_TRANS_TABLES,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION’;&lt;/code&gt; (WARNING: check the modes correspond with your setup.)&lt;/li&gt;
&lt;li&gt;Restart server:&lt;br&gt;&lt;code&gt;sudo systemctl start mysql&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="set-mysql-config-file"&gt;Option 2: setting the sql_mode in the MySQL configuration file&lt;/h3&gt;
&lt;h4 id="locating-mysql-conf"&gt;Locating the MySQL configuration file&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;my.cnf&lt;/code&gt; configuration file isn’t always found in the same place. It’s specific to the Linux distribution and server configuration but can normally found in one of the following locations:&lt;/p&gt;
&lt;pre class="wp-block-preformatted"&gt;/etc/my.cnf
/etc/mysql/my.cnf
echo/my.cnf
[datadir]/my.cnf
~/.my.cnf&lt;/pre&gt;

&lt;p&gt;If you can’t find your MySQL configuration file, you can try running &lt;code&gt;locate my.cnf&lt;/code&gt; or &lt;code&gt;mysqladmin –help&lt;/code&gt;. The latter will show something like the following in the output:&lt;/p&gt;

&lt;pre class="wp-block-preformatted"&gt;Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf&lt;/pre&gt;
&lt;p&gt;Also keep in mind that it’s possible to use &lt;code&gt;!include&lt;/code&gt; directives to include other &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/option-files.html" rel="noreferrer noopener" target="_blank"&gt;option files&lt;/a&gt; and &lt;code&gt;!includedir&lt;/code&gt; to search specific directories for option files. Under Ubuntu, there may be a file &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt; with &lt;code&gt;!includedir&lt;/code&gt; directives to search &lt;code&gt;/etc/mysql/conf.d/&lt;/code&gt; and &lt;code&gt;/etc/mysql/mysql.conf.d/&lt;/code&gt;&lt;/p&gt;


&lt;h4 id="edit-mysql-config-file"&gt;Editing the MySQL configuration file&lt;/h4&gt;
&lt;p&gt;If the MySQL server finds more than one configuration file, it will load each one in turn. The values override each other and it can be difficult to know which takes priority. Furthermore, the &lt;code&gt;–defaults-file&lt;/code&gt; parameter can also override all configurations. Keep things simple and have only one file and place it the directory that makes sense to you.&lt;/p&gt;
&lt;p&gt;Before editing the &lt;code&gt;my.cnf&lt;/code&gt;, first log in to MySQL with an administrator user and run the &lt;code&gt;SELECT @@GLOBAL.sql_mode&lt;/code&gt; query to see the values used in your setup.&lt;/p&gt;

&lt;figure class="wp-block-image size-full"&gt;&lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-mysql-select-global-sql_mode.jpg" alt="Setting the MySQL database server sql mode on Ubuntu for Drupal to WordPress migrations" class="wp-image-3705"&gt;&lt;br&gt;
&lt;figcaption class="wp-element-caption"&gt;&lt;em&gt;Running the &lt;code&gt;SELECT @@GLOBAL.sql_mode&lt;/code&gt; query on MySQL Workbench&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, open the configuration file, look for the section &lt;code&gt;[mysqld]&lt;/code&gt; and edit the line starting with:&lt;br&gt;&lt;code&gt;sql_mode = …&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add the line if it’s not there. Adjust the exact modes to match your project’s needs so take a look at the &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-full" target="_blank" rel="noreferrer noopener"&gt;list of SQL modes&lt;/a&gt; to see which may apply. I’ve found the following works well:&lt;br&gt;&lt;code&gt;sql_mode = “STRICT_TRANS_TABLES,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION”&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Finally, restart the MySQL server. On Ubuntu this will probably be with&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo systemctl start mysql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you are logged in to the MySQL server, you may also need to disconnect your client and reconnect for the changes to take effect for your session.&lt;/p&gt;

&lt;h2 id="potential-errors"&gt;Potential errors&lt;/h2&gt;
&lt;p&gt;Here are some potential errors that you may come across during a CMS migration project. I usually find them when running a Drupal to WordPress migration on a freshly built development environment.&lt;/p&gt;
&lt;h3 id="mysqldump-access-denied-when-trying-to-dump-tablespaces"&gt;mysqldump access denied when trying to dump tablespaces&lt;/h3&gt;
&lt;p&gt;Migrations involve dumping and importing databases and this process is straightforward on a mature development environment. However you may receive an ‘Access denied’ error out of the blue when dumping your MySQL database:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mysqldump: Error: ‘Access denied; you need (at least one of) the PROCESS privilege(s) for this operation’ when trying to dump tablespaces&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you see this, perhaps you were working on an environment that was a little too ‘mature’ (in other words, obsolete!) and you have recently upgraded your installation. The updates for MySQL 5.7.31 and MySQL 8.0.21 in July 2020 introduced an incompatible change that produces this error.&lt;/p&gt;
&lt;p&gt;Read my separate article, &lt;a href="https://anothercoffee.net/how-to-fix-the-mysqldump-access-denied-process-privilege-error/"&gt;How to fix the mysqldump access denied process privilege error&lt;/a&gt;, for more information and instructions on how you can try solving this.&lt;/p&gt;
&lt;h3 id="ERROR-1067-42000-Invalid-default-value"&gt;ERROR 1067 (42000) Invalid default value&lt;/h3&gt;
&lt;p&gt;Drupal nodes store the date as a Unix timestamp in an &lt;code&gt;int&lt;/code&gt; (e.g. &lt;code&gt;1623427200&lt;/code&gt;) field whereas WordPress stores dates as &lt;code&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.0/en/datetime.html" target="_blank" rel="noreferrer noopener nofollow"&gt;datetime&lt;/a&gt;&lt;/code&gt; (e.g. &lt;code&gt;2021-06-11 16:00:00&lt;/code&gt;). There may be a conversion error in your migration script or the source date could simply be zero for some reason. Normally your MySQL server mode will be set to &lt;code&gt;NO_ZERO_DATE, NO_ZERO_IN_DATE&lt;/code&gt; so trying to insert a zero date will give you the error:&lt;/p&gt;
&lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;ERROR 1067 (42000) Invalid default value&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can fix this by replacing &lt;code&gt;NO_ZERO_DATE, NO_ZERO_IN_DATE&lt;/code&gt;  with set to &lt;code&gt;ALLOW_INVALID_DATES&lt;/code&gt; in your global SQL mode.&lt;/p&gt;

&lt;h3 id="Expression-1-of-SELECT-list-is-not-in-GROUP-BY-clause"&gt;Expression #1 of SELECT list is not in GROUP BY clause&lt;/h3&gt;
&lt;p&gt;You run an SQL query and get the rather cryptic error:&lt;/p&gt;
&lt;pre class="wp-block-preformatted"&gt;Expression #1 of SELECT list is not in GROUP BY clause and contains
nonaggregated column ‘database.table.pid’ which is not functionally dependent
on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by&lt;/pre&gt;
&lt;p&gt;What’s wrong? MySQL has a &lt;code&gt;only_full_group_by&lt;/code&gt; mode which, when enabled, strictly applies ANSI SQL rules when using &lt;code&gt;GROUP BY&lt;/code&gt;. Fix this by reworking your script or removing the &lt;code&gt;ONLY_FULL_GROUP_BY&lt;/code&gt; SQL mode.&lt;/p&gt;

&lt;h3 id="Error-Code-2013-Lost-connection-to-MySQL-server"&gt;Error Code: 2013. Lost connection to MySQL server&lt;/h3&gt;
&lt;p&gt;This often happens when it an SQL query takes too long to return data. The connection between your MySQL client and database server times out so the connection gets dropped. For ideas on how to try solving this, read my separate article, &lt;a href="https://anothercoffee.net/fix-error-code-2013-lost-connection-mysql-server-query/"&gt;How to fix Error Code 2013 Lost connection to MySQL server&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Setting up a MySQL database server for Drupal or WordPress is a familiar task for web developers and site administrators.  However, CMS migrations have quirks that can cause obscure and baffling errors. In this guide I’ve shown you some little tricks that may save time and annoyance. While there’s no way to provide an exhaustive list of solutions to all the MySQL problems you’ll encounter, I hope to have pointed you in the right direction.&lt;/p&gt;
&lt;p&gt;If you have a site migration project and would like to hire me, please &lt;a href="https://anothercoffee.net/#contact"&gt;ask for a quote&lt;/a&gt; for my consulting service.&lt;/p&gt;

&lt;div class="alert alert-info text-center" style="margin: 100px 0 100px 0; padding-bottom:20px;"&gt;
&lt;h3 style="font-size: 1.5em;"&gt;&lt;a title="CMS migration" href="https://anothercoffee.net/drupal-to-wordpress-migration-service/"&gt;CMS migration consulting&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;All content · Custom content types · SEO · Plugins&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Migrating content from a site and need a specialist? Please &lt;a title="Get an estimate for site migration" href="https://anothercoffee.net/#contact"&gt;contact me&lt;/a&gt; for a quotation.&lt;/em&gt; Whether you’re a media agency who needs a database expert or a site owner looking for advice, I’ll save you time and ensure accurate content exports.&lt;/p&gt;

&lt;button class="cta-button"&gt;&lt;a href="https://anothercoffee.net/#contact"&gt;Get a quote&lt;/a&gt;&lt;/button&gt;

&lt;/div&gt;



&lt;div class="footnotes"&gt;
    &lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@rubaitulazad?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Rubaitul Azad&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/text-Y9kOsyoWyaU?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" target="_blank" rel="nofollow"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h2&gt;You may also like&lt;/h2&gt;

    &lt;div class="row"&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                        &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                        &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/section&gt;</description><category>Blog</category><category>Database</category><category>Drupal</category><category>Help</category><category>Migration</category><category>MySQL</category><category>Sys Admin</category><category>Troubleshooting</category><category>WordPress</category><guid>https://anothercoffee.net/how-to-set-up-a-mysql-database-server-on-ubuntu-for-drupal-to-wordpress-migrations/</guid><pubDate>Fri, 11 Jun 2021 15:44:52 GMT</pubDate></item><item><title>Ubercart to WooCommerce migration notes</title><link>https://anothercoffee.net/ubercart-to-woocommerce-migration/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;I've now had several Ubercart to WooCommerce migration projects so it's time to start documenting the process. As with all my documentation, I'll start off this post as rough notes and improve it over time. If this topic interests you, be sure to check back every so often to see the updates. Please keep in mind that I'm writing this from the standpoint of &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" title="Drupal to WordPress Migration Guide"&gt;Drupal to WordPress migrations&lt;/a&gt;. In other words, the main objective for the projects were to migrate a Drupal site to WordPress but there was also an Ubercart to WooCommerce component.&lt;/p&gt;

&lt;h2&gt;Key differences between Ubercart and WooCommerce&lt;/h2&gt;

&lt;p&gt;There's one key difference between Ubercart and WooCommerce from an architectural point-of-view. Ubercart stores products as nodes and orders are stored in a separate table &lt;code&gt;uc_orders&lt;/code&gt;. Orders have an &lt;code&gt;order_id&lt;/code&gt; and there's no direct relationship in &lt;code&gt;uc_orders&lt;/code&gt; to a product's node ID. Instead, the &lt;code&gt;uc_order_products&lt;/code&gt; table stores the relationship between orders and products purchased with the order.&lt;/p&gt;

&lt;p&gt;WooCommerce stores both products and order transactions as posts in the &lt;code&gt;wp_posts&lt;/code&gt; table. The post ID is used as the order's transaction ID. Products purchased with the order are stored in &lt;code&gt;wp_woocommerce_order_items&lt;/code&gt;, with additional product metadata being strored in &lt;code&gt;wp_woocommerce_order_itemmeta&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Obviously there are many more differences but this information architecture is the key thing to keep in mind when migrating the data.&lt;/p&gt;

&lt;img style="margin:10px 0 10px 0" src="https://anothercoffee.net/images/ubercart-woocommerce-01.png" alt="Ubercart to WooCommerce table mapping" width="481" height="155" class="aligncenter size-full wp-image-3069"&gt;

&lt;h2&gt;Database tables&lt;/h2&gt;

&lt;p&gt;Here are the main database tables that you'll need to migrate Ubercart content to WooCommerce.&lt;/p&gt;

&lt;h3&gt;Ubercart&lt;/h3&gt;

&lt;table width="100%" class="table-bordered" style="padding:5px"&gt;
&lt;caption&gt;Table: Drupal Ubercart tables&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="50%"&gt;Table&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;node&lt;/td&gt;
&lt;td&gt;Products are stored in the Drupal &lt;code&gt;node&lt;/code&gt; table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;uc_orders&lt;/td&gt;
&lt;td&gt;Stores the individual Ubercart transactions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;uc_order_products&lt;/td&gt;
&lt;td&gt;Stores the products purchased during the transaction.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;uc_order_line_items&lt;/td&gt;
&lt;td&gt;Line items for an order. This includes tax and shipping fees applied to an order.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;uc_order_comments&lt;/td&gt;
&lt;td&gt;Customer or administrator notes associated with each order.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;uc_order_log&lt;/td&gt;
&lt;td&gt;Comments about the order status by the shop administrator.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;uc_zone&lt;/td&gt;
&lt;td&gt;Country zone codes for customer billing and delivery.&lt;/td&gt;
&lt;/tr&gt;&lt;tr class="even"&gt;
&lt;td&gt;uc_countries&lt;/td&gt;
&lt;td&gt;Countries and zone codes in ISO 3166-1 Alpha-2 and Alpha-3 code format for customer billing and delivery.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;



&lt;h3&gt;WooCommerce&lt;/h3&gt;

&lt;table width="100%" class="table-bordered"&gt;
&lt;caption&gt;Table: WordPress WooCommerce tables&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="50%"&gt;Table&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;wp_posts&lt;/td&gt;
&lt;td&gt;Stores products and transactions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;wp_postmeta&lt;/td&gt;
&lt;td&gt;Transaction meta data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;wp_woocommerce_order_items&lt;/td&gt;
&lt;td&gt;Stores the line items for a transaction in the &lt;code&gt;wp_posts&lt;/code&gt; table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;wp_woocommerce_order_itemmeta&lt;/td&gt;
&lt;td&gt;Stores the meta data for line items, such as quantity, price and tax information.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;wp_comments&lt;/td&gt;
&lt;td&gt;Order notes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Order transactions and products are saved as WordPress posts. One or many order item meta entries can be linked to an order.&lt;/p&gt;

&lt;h2&gt;WooCommerce shop orders&lt;/h2&gt;

A WooCommerce transaction is saved as a shop order in the &lt;code&gt;wp_posts&lt;/code&gt; table.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;post_status = wc_completed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_type = shop_order&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;WooCommerce subscriptions&lt;/h2&gt;

&lt;p&gt;WooCommerce subscriptions are saved in &lt;code&gt;wp_posts&lt;/code&gt;. A subscription is made up of two entries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The transaction order for a subscription.&lt;/li&gt;
&lt;li&gt;The subscription itself.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;Subscription transaction order&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;post_status = wc_completed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_parent = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_type = shop_order&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The subscription&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;post_status = wc_active&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_parent = [post ID to the transaction shop order]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_type = shop_subscription&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!--
When adding a subscription order's product data to the &lt;code&gt;post_type=shop_subscription&lt;/code&gt; it's important to attach products to the &lt;code&gt;shop_subscription&lt;/code&gt; ID not the &lt;code&gt;shop_order&lt;/code&gt; ID, otherwise the subscription title will not be displayed in the user's &lt;em&gt;My Account&lt;/em&gt; page.
--&gt;
&lt;h2&gt;Products&lt;/h2&gt;

Purchased products are saved as line items in the &lt;code&gt;wp_woocommerce_order_items&lt;/code&gt; and &lt;code&gt;wp_woocommerce_order_itemmeta&lt;/code&gt; tables.

&lt;p&gt;A WooCommerce product can be of any post type. The product post is linked to a shop order transaction by setting the product's post ID in the &lt;code&gt;wp_woocommerce_order_itemmeta&lt;/code&gt; table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;meta_key = _product_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;meta_value = [post ID to the product]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr style="margin-top:5em"&gt;
&lt;h3 id="notes"&gt;Notes&lt;/h3&gt;

&lt;p&gt;My apologies if you've come here looking for more complete documentation. I've been planning to write this post for more than two years but have been putting it off due to my work schedule. I figure the best way to finally get it done is to just make a start and update it as time allows.&lt;/p&gt;

&lt;p&gt;The most-viewed articles here started off as notes for my own use and evolved over time. This one is no different. Hopefully it will still be of use to some people in its draft state.&lt;/p&gt;

&lt;p&gt;In the meantime, you might want to take a look at these other articles and plugins.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.webtoffee.com/export-woocommerce-orders-import-back-new-website/"&gt;How to Export WooCommerce Orders and Import Them Back into New Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sgwebpartners.com/moving-orders-in-woocommerce/"&gt;How To Migrate WooCommerce Customers and Orders (Like A Pro!)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.skyverge.com/blog/moving-woocommerce-orders-sites/"&gt;Moving WooCommerce Orders Between Sites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://woocommerce.com/products/ordercustomer-csv-export/#"&gt;WooCommerce Customer / Order / Coupon Export&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wordpress.org/plugins/woocommerce-sequential-order-numbers/"&gt;WooCommerce Sequential Order Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://woocommerce.com/products/customerorder-csv-import-suite/#"&gt;Customer/Order/Coupon CSV Import Suite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><category>Drupal</category><category>Ecommerce</category><category>Migration</category><category>Ubercart</category><category>WooCommerce</category><category>WordPress</category><guid>https://anothercoffee.net/ubercart-to-woocommerce-migration/</guid><pubDate>Fri, 24 Jan 2020 12:14:07 GMT</pubDate></item><item><title>How to fix Error Code 2013 Lost connection to MySQL server</title><link>https://anothercoffee.net/fix-error-code-2013-lost-connection-mysql-server-query/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;If you spend time running lots of MySQL queries, you might come across the &lt;code&gt;Error Code: 2013. Lost connection to MySQL server during query&lt;/code&gt;. This article offers some suggestions on how to avoid or fix the problem.&lt;/p&gt;
&lt;h2&gt;Why this happens&lt;/h2&gt;
&lt;p&gt;This error appears when the connection between your MySQL client and database server times out. Essentially, it took too long for the query to return data so the connection gets dropped.&lt;/p&gt;
&lt;p&gt;Most of my work involves &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;content migrations&lt;/a&gt;. These projects usually involve running complex MySQL queries that take a long time to complete. I’ve found the WordPress &lt;em&gt;wp_postmeta&lt;/em&gt; table especially troublesome because a site with tens of thousands of posts can easily have several hundred thousand postmeta entries. Joins of large datasets from these types of tables can be especially intensive.&lt;/p&gt;
&lt;h2&gt;Avoid the problem by refining your queries&lt;/h2&gt;
&lt;p&gt;In many cases, you can avoid the problem entirely by refining your SQL queries. For example, instead of joining all the contents of two very large tables, try filtering out the records you don’t need. Where possible, try reducing the number of joins in a single query. This should have the added benefit of making your query easier to read. For my purposes, I’ve found that denormalizing content into working tables can improve the read performance. This avoids time-outs.&lt;/p&gt;
&lt;p&gt;Re-writing the queries isn’t always option so you can try the following server-side and client-side workarounds.&lt;/p&gt;
&lt;h2&gt;Server-side solution&lt;/h2&gt;
&lt;p&gt;If you’re an administrator for your MySQL server, try changing some values. The MySQL &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/error-lost-connection.html"&gt;documentation suggests&lt;/a&gt; increasing the &lt;code&gt;net_read_timeout&lt;/code&gt; or &lt;code&gt;connect_timeout&lt;/code&gt; values on the server.&lt;/p&gt;
&lt;h2&gt;Client-side solution&lt;/h2&gt;
&lt;p&gt;You can increase your MySQL client’s timeout values if you don’t have administrator access to the MySQL server.&lt;/p&gt;
&lt;h3&gt;MySQL Workbench&lt;/h3&gt;
&lt;p&gt;You can edit the SQL Editor preferences in MySQL Workbench:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the application menu, select &lt;em&gt;Edit &amp;gt; Preferences &amp;gt; SQL Editor&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Look for the &lt;em&gt;MySQL Session&lt;/em&gt; section and increase the &lt;em&gt;DBMS connection read time out value&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Save the settings, quite MySQL Workbench and reopen the connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="centred-image-container"&gt;
    &lt;img class="centred-image" width="635" height="141" src="https://anothercoffee.net/images/mysql-workbench-session.png" alt="MySQL Workbench session settings"&gt;
&lt;/div&gt;

&lt;h3&gt;Navicat&lt;/h3&gt;
&lt;p&gt;How to edit Navicat preferences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Control-click on a connection item and select &lt;em&gt;Connection Properties &amp;gt; Edit Connection&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;Advanced&lt;/em&gt; tab and increase the &lt;em&gt;Socket Timeout&lt;/em&gt; value.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Command line&lt;/h3&gt;
&lt;p&gt;On the command line, use the &lt;code&gt;&lt;a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-command-options.html#option_mysql_connect_timeout"&gt;connect_timeout&lt;/a&gt;&lt;/code&gt; variable.&lt;/p&gt;
&lt;h3&gt;Python script&lt;/h3&gt;
&lt;p&gt;If you’re running a query from a Python script, use the connection argument:&lt;br&gt;
&lt;code&gt;con.query(‘SET GLOBAL connect_timeout=6000′)&lt;/code&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h2&gt;You may also like&lt;/h2&gt;

    &lt;div class="row"&gt;
        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                        &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/still-alive-a-micro-agencys-20-year-journey/"&gt;
                        &lt;img src="https://anothercoffee.net/images/a-west-london-micro-agencys-journey-to-ai-featured.jpg" class="card-img-top" alt="Still Alive: A Micro Agency's 20 Year Journey"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/still-alive-a-micro-agencys-20-year-journey/" class="listtitle"&gt;Still Alive: A Micro Agency's 20 Year Journey&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-10-15T15:28:15Z" title="15 October 2024"&gt;15 October 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;This article will be the first in a series where I'll share how Artificial Intelligence has reshaped how we operate at Another Cup of Coffee.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/section&gt;</description><category>Blog</category><category>Drupal</category><category>Help</category><category>Migration</category><category>Troubleshooting</category><category>WordPress</category><guid>https://anothercoffee.net/fix-error-code-2013-lost-connection-mysql-server-query/</guid><pubDate>Sat, 15 Jul 2017 19:08:23 GMT</pubDate></item><item><title>Post-migration troubleshooting: Gateway timeout when enabling plugins</title><link>https://anothercoffee.net/post-migration-troubleshooting-gateway-timeout-enabling-plugins/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;Here's one that caught me out on a recent Drupal to WordPress migration. As is common with my projects, there were three parties involved: the client, an external development team and myself. The WordPress site was first built on the development team's server, after which it was migrated to my local environment. When everything was ready for beta testing, we moved the site over to the client's staging server on a newly activated account over at &lt;a href="https://kinsta.com/"&gt;Kinsta&lt;/a&gt;. Eventually, we'd move it over to a live server, also hosted on Kinsta.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-team-structure.jpg" title="Drupal to WordPress migration workflow" alt="Diagram of Drupal to WordPress migration workflow for this project" width="600" height="273" class="aligncenter size-full wp-image-1980"&gt;&lt;/p&gt;

&lt;p&gt;After some initial tests on staging, I found that deactivating and reactivating plugins would cause the site to hang and show a &lt;em&gt;'504 Gateway Time-out'&lt;/em&gt; error. This happened when re-enabling some but not all plugins.&lt;/p&gt;

&lt;p&gt;I initially suspected some misconfiguration at the hosting end because there were some hitches with the newly create account. At the outset, the account's server had an issue which Kinsta support needed to fix. For convenience, we'd also made use of Kinsta's free site migration service. This is where they'll migrate an existing WordPress site into their environment. Though it would have been easy enough for me to do, we thought to give it a try. In hindsight, this was a bit of a mistake. The site migration service itself was fine but it did end up causing some confusion. First, a miscommunication in the migration request caused them to create a temporary domain we didn't want. They helpfully solved this by giving us a second temporary domain. However, they'd also upgraded everything to PHP 7 in the process. All of these issues were possible suspects for the time-out error but turned out to be red-herrings.&lt;/p&gt;

&lt;p&gt;It took some time to pinpoint the cause behind the Gateway Time-out error. I do have to say that Kinsta support were very responsive throughout the troubleshooting process. They eventually put a senior engineer on the case who found the problem. It turned out the problem wasn't to do with Kinsta at all. There was a leftover setting from the original development team's server. It was a valid format so didn't cause an issue on either my local server or my staging server. However, it apparently can cause issues with plugins and did on the Kinsta environment.&lt;/p&gt;

&lt;p&gt;What was the setting? The WordPress upload path directory was set to the development team's server path e.g. &lt;code&gt;/home/dev/public_html/sitename&lt;/code&gt;. Throughout the migration, I'd been doing a database search-and-replace looking for their development domain. Somehow, as the site moved from different servers, that server path string remained in the database, only to cause a problem when the site landed in the destination server on Kinsta.&lt;/p&gt;

&lt;p&gt;I'm not sure if there would have been any way to have caught this problem earlier. It's one of those obscure errors that are easy to overlook and take time to resolve. There's also no practical way to do a database search-and-replace for every imaginable string. I'll have to rack this one up to experience. &lt;/p&gt;</description><category>Drupal</category><category>Help</category><category>Migration</category><category>Troubleshooting</category><category>WordPress</category><guid>https://anothercoffee.net/post-migration-troubleshooting-gateway-timeout-enabling-plugins/</guid><pubDate>Mon, 05 Jun 2017 09:35:07 GMT</pubDate></item><item><title>How to change the WordPress table prefix prior to a migration</title><link>https://anothercoffee.net/change-wordpress-table-prefix-prior-migration/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;When working on a &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-service/"&gt;Drupal to WordPress migration&lt;/a&gt; project, I like to migrate into a set of intermediary WordPress tables that live in the Drupal database. These are working tables where I can run various scripts to process and clean up the content before exporting to a working WordPress installation. It’s not &lt;em&gt;necessary&lt;/em&gt; to do this but I find it convenient to run scripts on the same database rather than deal with two separate database connections.&lt;/p&gt;


&lt;figure class="figure d-flex flex-column align-items-center"&gt;
    &lt;img src="https://anothercoffee.net/images/gavin-allanwood-q92hWEdK8p8-unsplash.jpg" alt="Overhead image of cafe tables" class="figure-img img-fluid rounded" width="800" height="533"&gt;
&lt;/figure&gt;

&lt;p&gt;Note that some people suggest renaming the table prefixes to improve security. My use of the table prefixes is simply to create temporary containers for the migration. While non-standard prefixes might help prevent ‘script kiddie’ attacks, I find it isn’t worth the disadvantages that come with this sort of &lt;a href="https://en.wikipedia.org/wiki/Security_through_obscurity"&gt;security through obscurity&lt;/a&gt; (or more precisely, &lt;a href="https://en.wikipedia.org/wiki/Security_through_obscurity#Security_through_minority"&gt;security through minority&lt;/a&gt;) approach. Here are two articles give a deeper explanation of topic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wordfence.com: &lt;a href="https://www.wordfence.com/blog/2016/12/wordpress-table-prefix/"&gt;WordPress Table Prefix: Changing It Does Nothing to Improve Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;wpkrauts.com: &lt;a href="https://web.archive.org/web/20150129082218/http://wpkrauts.com/2015/the-database-prefix-is-not-a-security-feature/"&gt;The database table prefix is not a security feature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SQL queries to change the WordPress table prefixes&lt;/h2&gt;
&lt;p&gt;You can start with a freshly installed WordPress database. Dumping this and importing to your Drupal migration database will give you all the tables with the correct WordPress schema. I use the &lt;em&gt;acc_&lt;/em&gt; prefix but you can use whatever you want.&lt;/p&gt;
&lt;p&gt;Rename the tables with these queries:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RENAME table `wp_commentmeta` TO `acc_commentmeta`;
RENAME table `wp_comments` TO `acc_comments`;
RENAME table `wp_links` TO `acc_links`;
RENAME table `wp_options` TO `acc_options`;
RENAME table `wp_postmeta` TO `acc_postmeta`;
RENAME table `wp_posts` TO `acc_posts`;
RENAME table `wp_terms` TO `acc_terms`;
RENAME table `wp_termmeta` TO `acc_termmeta`;
RENAME table `wp_term_relationships` TO `acc_term_relationships`;
RENAME table `wp_term_taxonomy` TO `acc_term_taxonomy`;
RENAME table `wp_usermeta` TO `acc_usermeta`;
RENAME table `wp_users` TO `acc_users`;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing the migration results&lt;/h2&gt;
&lt;p&gt;If you need to point a WordPress installation to these tables for testing, you’ll need to do two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the &lt;em&gt;$table_prefix&lt;/em&gt; setting in the &lt;em&gt;wp-options.php&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;options&lt;/em&gt; and &lt;em&gt;usermeta&lt;/em&gt; tables&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Updating the &lt;em&gt;$table_prefix&lt;/em&gt; setting in the &lt;em&gt;wp-options.php&lt;/em&gt; file is straightforward. Open the file and edit the line:&lt;/p&gt;
&lt;pre&gt;$table_prefix  = ‘acc_’;&lt;/pre&gt;
&lt;p&gt;In WordPress, prefixes are saved as entries in the &lt;em&gt;options&lt;/em&gt; and &lt;em&gt;usermeta&lt;/em&gt; table. Check for entries containing the prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM `acc_options` WHERE `option_name` LIKE ‘%wp_%’;
SELECT * FROM `acc_usermeta` WHERE `meta_key` LIKE ‘%wp_%’;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you have all the entries, update them with the new prefix. The query will probably look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UPDATE `acc_options` SET `option_name` = ‘acc_user_roles’ WHERE `option_name` = ‘wp_user_roles’;
UPDATE `acc_usermeta` SET `meta_key` = ‘acc_capabilities’ WHERE `meta_key` = ‘wp_capabilities’;
UPDATE `acc_usermeta` SET `meta_key` = ‘acc_user_level’ WHERE `meta_key` = ‘wp_user_level’;
UPDATE `acc_usermeta` SET `meta_key` = ‘acc_user-settings-time’ WHERE `meta_key` = ‘wp_user-settings-time’;
UPDATE `acc_usermeta` SET `meta_key` = ‘acc_user-settings’ WHERE `meta_key` = ‘wp_user-settings’;
UPDATE `acc_usermeta` SET `meta_key` = ‘acc_dashboard_quick_press_last_post_id’ WHERE `meta_key` = ‘wp_dashboard_quick_press_last_post_id’;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;A word of warning:&lt;/strong&gt; it’s easy to forget to change the prefixes back to match the final WordPress installation. If you do, the WordPress user accounts will have problems, such as the &lt;a href="http://192.168.1.30:8100/drupal-to-wordpress-migration-notes/"&gt;Dashboard controls not being visible&lt;/a&gt; after logging in. Because of this, I tend to have a separate testing installation that gets an import of the working tables.&lt;/p&gt;

&lt;div class="footnotes"&gt;
&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@gavla?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" target="_blank" rel="nofollow"&gt;Gavin Allanwood&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/aerial-photo-of-grey-tables-q92hWEdK8p8?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" target="_blank" rel="nofollow"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;


&lt;hr&gt;

&lt;section class="mt-4 pt-4"&gt;
    &lt;h2&gt;You may also like&lt;/h2&gt;

    &lt;div class="row"&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/"&gt;
                        &lt;img src="https://anothercoffee.net/images/drupal-to-wordpress-migration-utilities-featured.jpg" class="card-img-top" alt="Drupal to WordPress Migration Guide"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-to-wordpress-migration-guide/" class="listtitle"&gt;Drupal to WordPress Migration Guide&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2025-01-03T15:30:30Z" title="Updated for 2025"&gt;Updated for 2025&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;p class="card-text flex-grow-1"&gt;In this guide, you'll find insights drawn from almost 15 years of specialising in complex Drupal to WordPress migration projects. I'll walk you through the entire migration process, from the initial evaluation to post-launch considerations.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/"&gt;
                        &lt;img src="https://anothercoffee.net/images/Secure-your-AI-workflow-using-local-tokenisation-in-PaigeSafe-featured.jpg" class="card-img-top" alt="Secure Your AI Workflow Using Local Tokenisation"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/secure-your-ai-workflow-using-local-tokenisation/" class="listtitle"&gt;Secure Your AI Workflow Using Local Tokenisation&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-11-12T13:59:03Z" title="12 November 2024"&gt;12 November 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Don't leak confidential client data when using cloud-based LLMs. Secure your AI workflow with local tokenisation using PaigeSafe.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;div class="col-md-6 col-lg-4 mb-4"&gt;
            &lt;div class="card h-100"&gt;
                &lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/"&gt;
                        &lt;img src="https://anothercoffee.net/images/posts/Drupal-Docker-Containers-card-300-150.jpg" class="card-img-top" alt="How To Set Up Drupal 7 Docker Containers for Migration Projects"&gt;&lt;/a&gt;
                &lt;div class="card-body d-flex flex-column"&gt;
                    &lt;h5 class="card-title"&gt;&lt;a href="https://anothercoffee.net/drupal-7-docker-containers-migration-projects/" class="listtitle"&gt;How To Set Up Drupal 7 Docker Containers for Migration Projects&lt;/a&gt;&lt;/h5&gt;
                    &lt;div class="mb-2"&gt;
                        &lt;span&gt;&lt;time class="listdate" datetime="2024-09-09T13:25:15Z" title="09 September 2024"&gt;09 September 2024&lt;/time&gt;&lt;/span&gt;
                    &lt;/div&gt;

                        &lt;p class="card-text flex-grow-1"&gt;Learn how Docker is a valuable tool for Drupal 7 end of life migrations. In this post, I'll give a step-by-step guide to setting up a Drupal 7 container for your migration project.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;

&lt;/section&gt;</description><category>Blog</category><category>Drupal</category><category>Help</category><category>Migration</category><category>WordPress</category><guid>https://anothercoffee.net/change-wordpress-table-prefix-prior-migration/</guid><pubDate>Thu, 16 Mar 2017 13:12:02 GMT</pubDate></item><item><title>Migrating Bare Bones Yojimbo to WordPress</title><link>https://anothercoffee.net/migrating-bare-bones-yojimbo-wordpress/</link><dc:creator>Anthony Lopez-Vito</dc:creator><description>&lt;p&gt;In a &lt;a href="https://anothercoffee.net/yojimbo-to-tomboy-notes-migration-tool/"&gt;previous post&lt;/a&gt;, I wrote about exporting data from Bare Bones Software's Yojimbo and using &lt;a href="https://wiki.gnome.org/Apps/Tomboy"&gt;Tomboy&lt;/a&gt; as an alternative. My migration script scraped the content from Yojimbo Sidekick and wrote XML files in Tomboy Note format. Though there were some drawbacks, such as tags being unavailable in Yojimbo Sidekick, I thought Tomboy's search feature would be adequate. A couple of weeks trialling Tomboy proved that it wasn't going to be a Yojimbo killer. Tomboy can't compete in terms of overall usability and though it'll work on my Linux and OS X machines, note synchronisation takes &lt;a href="https://wiki.gnome.org/Apps/Tomboy/Synchronization"&gt;some setup&lt;/a&gt; that didn't warrant further time investment.&lt;/p&gt;

&lt;p&gt;Once again I turned to WordPress as an easy solution. There's a risk of seeing WordPress as my hammer for everything that looks like a nail but it has taxonomies, a reliable-enough search functionality and being web-based, works across all my devices. I'm very familiar with the platform and have already built up my own set of tools to export and migrate content. Why &lt;em&gt;not&lt;/em&gt; use WordPress? Getting data out of Yojimbo was another issue. The quick and easy Yojimbo Sidekick route already proved inadequate so it was time to dig in and reverse engineer Yojimbo's storage mechanism.&lt;/p&gt;

&lt;h2&gt;Analysing and exporting the Yojimbo database&lt;/h2&gt;

&lt;p&gt;'Reverse engineering' turned out to be too lofty a term for the task. It was obvious after quick look that Yojimbo uses an SQLite database to store information. Firing up &lt;a href="http://sqlitebrowser.org/" title="DB Browser for SQLite "&gt;DB4S&lt;/a&gt; to analyse the tables and bit of analysis revealed the tables, columns and relationships that are important for exporting our notes. The columns are a little oddly named but it didn't take long to figure out the necessary fields for migrating to WordPress.&lt;/p&gt;

&lt;table width="60%" class="table-bordered"&gt;
&lt;caption&gt;Table: ZITEM&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="10%"&gt;Column&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;ZBLOB&lt;/td&gt;
&lt;td&gt;This looks like an ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;ZNAME&lt;/td&gt;
&lt;td&gt;The Yojimbo note title&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;Z_PK&lt;/td&gt;
&lt;td&gt;The ID to the Z_15TAGS relationship table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table width="60%" class="table-bordered"&gt;
&lt;caption&gt;Table: ZTAG&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="10%"&gt;Column&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;Z_PK&lt;/td&gt;
&lt;td&gt;The Tag ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;ZNAME&lt;/td&gt;
&lt;td&gt;The tag name&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table width="60%" class="table-bordered"&gt;
&lt;caption&gt;Table: ZBLOBLSTRINGREP&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="10%"&gt;Column&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;ZBLOB&lt;/td&gt;
&lt;td&gt;ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;ZSTRING&lt;/td&gt;
&lt;td&gt;String for unencrypted item&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table width="60%" class="table-bordered"&gt;
&lt;caption&gt;Table: Z_15TAGS&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th width="10%"&gt;Column&lt;/th&gt;
&lt;th width="50%"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd row-first"&gt;
&lt;td&gt;Z_15ITEMS1&lt;/td&gt;
&lt;td&gt;Relationship ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;Z_25ITAGS&lt;/td&gt;
&lt;td&gt;Tag ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;img src="https://anothercoffee.net/images/yojimbo-tables.jpg" alt="Yojimbo SQLite tables" width="363" height="213" class="aligncenter size-full wp-image-1822"&gt;

&lt;p&gt;It became a simple matter of tweaking my Drupal to WordPress migration queries to extract from the Yojimbo database and create WordPress posts. Unlike with the Tomboy Notes route, it was possible to recreate the tags, which is what makes Yojimbo so useful. One drawback is that I haven't figured out how to extract encrypted notes but I didn't use Yojimbo to store important encrypted information so that wasn't a priority.&lt;/p&gt;

&lt;h2&gt;WordPress as a Yojimbo alternative&lt;/h2&gt;

&lt;p&gt;Using WordPress as a Yojimbo alternative might not work for everyone but after several months use, I've found it to be an excellent cross-platform replacement. The installation and database runs on a NAS drive connected to my local network so is accessible to all my devices. Standard WordPress taxonomies, search and plugins makes content management simple once you've imported the Yojimbo content. In fact, by migrating away from Yojimo, I've ended up creating my own full-blown personal knowledge management system.&lt;/p&gt;

&lt;p&gt;If you need to export your Yojimbo notes to a cross-platform alternative, give WordPress a try. You can grab my migration script from &lt;a href="https://gitlab.com/anthonylv/pyYojimboMigrate" title="pyYojimboMigrate: Yojimbo to WordPress export and migration"&gt;GitLab&lt;/a&gt; but please keep in mind that it was a quick hack to achieve a specific one-time objective. You may need to hack it to suit your own setup.&lt;/p&gt;</description><category>Database</category><category>Migration</category><category>SQL</category><category>Tomboy</category><category>WordPress</category><category>Yojimbo</category><guid>https://anothercoffee.net/migrating-bare-bones-yojimbo-wordpress/</guid><pubDate>Wed, 04 Jan 2017 11:28:07 GMT</pubDate></item></channel></rss>