Migrate from Beehive: Using Ghost-CMS

Hello 👋

This is the documentation of the challenge that Sandeep Das announced → see here

🎯 Agenda: I am documenting the whole process that I gone through from setting up Ghost-CMS to importing content from a Beehive to it -


🧐 Why I chose beehive?

It is not like I chose beehive for any particular reason I chose it because it is very popular, I am subscribed to some newsletter of some creators who uses these platform and Sandeep Das sir also migrated from this platform to their own who gave this challenge

(spoiler alert)🚨: I should have chosen substack because, this created a problem and wasted the 4 hours of mine for me you will come to know later in this blog but because of this it will be easier for you guys to migrate from beehiiv

anyway, lets continue ⏩


Now let’s Begin 🚀

Part - 1 → Beehive setup 🐝

(if you are already a user of beehive or any other newsletter platform then skip this part)

  1. go to beehiiv site and create an account and after creating the account you will see a dashboard like this

    adf

  2. Now, you have a post section in the sidebar just under the plus button go there and create some posts.

  3. Just under the post section in the sidebar you also have audience section from where you can import subscriber click on import subscribers and you will see screen like this

  4. Now, choose the import method as in this screen and you can add subscribers

  5. Now, you need to export the data from the beehiiv this is pretty straight forward,

    so you can see that export data is written just under the subscribers import so click on that and you will see

  6. Remember to import the subscribers data and Posts data separately.

  7. Now, you can explore according to your wants and needs this was all for the part - 1 😁

Part - 1 Mission Passed ✅


Part - II Begins 🎬

Now this is will be interesting -

  1. You have to set up the Ghost CMS locally there are more than one way to do it but the one which I found simple is documented here

  2. First, lets see see what all are the requirements we need 📋

    a. Node.js

    b. Database like mysql, postgresql etc

    c. Package manager like yarn or other

    MacOS (as this tutorial all commands are according to MacOS)

  3. Now let’s start with installation ⚙️

    1. go to terminal and run the command to install the ghost cli

      🧐Why? → it makes easier to install, configure and manage the ghost

       npm install -g ghost-cli
      
    2. Now create a folder manually or by command in your system

      🧐Why? → to provide a dedicated folder to install ghost locally and prevent any sort of conflicts and to organise the project correctly

       mkdir ghost-local
       cd ghost-local
      
    3. Now install the ghost locally

      🤨What will this command do?

      1. Installs Ghost in development mode using SQLite (no MySQL needed we will configure it later)

      2. Automatically handles dependencies

      3. Starts the local Ghost server

      4. Opens the admin panel in your browser

        ghost install local
  1. Access Ghost

  1. Now, these are some useful command of ghost 📋

    | Command | Why it's needed | | --- | --- | | ghost start | Start the local Ghost instance | | ghost stop | Stop the Ghost server | | ghost restart | Restart after making changes | | ghost log | Debug by viewing logs | | ghost update | Keep Ghost CMS up to date |

  1. Now, when you will go to the localhost url you will see a screen

    Using Ghost as a Headless CMS - Draft.dev

  2. You have to set it up by providing your site title, name, email and password, it is pretty straight forward like any other signing up process and also remember the password 😁 as you will need it later

  3. Now, you have signed up and you will be seeing a dashboard like

Now, we will be importing the data which we exported from the beehiiv

  1. There is a catch in this, remember I was telling I should have used substack now you will get to know why this beehiiv migration thing is not that straight forward

    1. Now, when you click on the setting icon this will be showed

    2. Now, when you scroll down in the sidebar in the advanced section of sidebar there is written import and export

    3. There are direct options for other platforms like substack, mailchimp etc. which is pretty simple but when you click on them they will only run if you are available online but there is a quick solution for that go to ngrok which will make your localhost available online and then you can use these options

      but there is no direct option for the beehiiv 😔

    4. So, I used Universal import but again it was a problem only, because the format in which it accepts and the format in which beehiiv exports are different so we need to convert the beehiiv files to right format and even when you do that a new problem will be waiting for you

      when you upload the json or zip file of the content and subscribers it will show

      “Import in Progress“ ⏸️ but this will never progress as in the logs you will see

      This error is coming because I haven’t set up any mail service but there is no option to bypass the mail service I was stuck in this problem for 3 hours 😔 but now, you don’t have to because this is not the correct way to import from beehiiv in the first place 😜, the correct way is below

  2. But, First let’s change our database to mysql there is no need but I prefer mysql because I am familiar with its workbench for doing this you need to first stop the ghost and then

    → open the ghost which you installed locally → open config.development.json file

    → then in that edit to make database

     {
       "database": {
         "client": "mysql",
         "connection": {
           "host": "localhost",
           "user": "ghost_user",
           "password": "your_secure_password",
           "database": "ghost_db"
         }
       }
     }
    

    now, you can start the ghost again

    but this is when you don’t already have any posts in the previous database if you have that then you need to migrate it from that database to this for that there are a couple of extra steps

  3. ‼️ this method is for importing the free subscribers and local set up if you have paid users and custom domain then you will require some more configurations for avoiding problems for that you can refer to this ghost-cms article for more details → here

    1. Importing Subscribers

      for this go to members section on the dashboard and you will see

      Click on settings icon and then on the import members and you will see a popup which will ask for csv, upload the csv which you have exported from the beehiive

      upload that csv and you will see some configurations you can edit them and after clicking on import button your subscribers will get imported

    2. Importing Posts

      1. now for migrating the posts we need to install a dependency to do that → run this command

         npm install --global @tryghost/migrate  # Install CLI
         # Verify it's installed
         migrate
        

        Now, run this command to make the zip file of the posts that you exported from the beehiiv

         # Basic migration
         migrate beehiiv --posts /path/to/posts.csv --url https://example.com
        

        in the —url you can use you localhost url

        ‼️ Now, this will create a zip file, now go back to that import/export in advanced section of the setting’s sidebar of ghost cms and upload this zip file and your posts will be visible in the ghost cms

        you will see a screen like this in when the command run is completed

  1. Now, you have imported the content now it is the time to tweak the design from default to give it personal touch to do so go to the site section in the setting

    there are good options to do that in the ghost cms but better ones are paid at least you have many option in choosing theme in theme section anyway, I briefly explored this section because design is subjective and we can keep changing further

  2. After this your site will be looking like this according to your theme you posts should be visible on http://localhost:2368/

Part - II → Concludes Here ✅

here, you have local set up of ghost cms with imported data


Part - III → Docker Compose File 🐳

I am not a pro in Docker but I studied docker through tutorials and I created it by viewing some of that tutorials and also A.I. for specific doubts. This may be not perfect or very optimised but
This is my docker compose file -

# Docker Compose version - tells Docker which features are available
version: '3.8'

# Services section - defines all the containers we want to run
services:

  # Ghost CMS container
  ghost:
    # Which Docker image to use - Ghost version 5 with Alpine Linux (lightweight)
    image: ghost:5-alpine

    # Container name for easier identification
    container_name: ghost-cms

    # Always restart if container stops/crashes
    restart: always

    # Port mapping: host_port:container_port
    # Maps port 2368 on your computer to port 2368 inside container
    ports:
      - "2368:2368"

    # Environment variables - configuration settings for Ghost
    environment:
      # The URL where your blog will be accessible
      url: http://localhost:2368

      # Production mode for better performance and security
      NODE_ENV: production

      # Database type - telling Ghost to use MySQL instead of default SQLite
      database__client: mysql

      # MySQL connection settings
      database__connection__host: mysql-db        # hostname of MySQL container
      database__connection__user: ghost_user      # MySQL username
      database__connection__password: ghost_password  # MySQL password
      database__connection__database: ghost_db    # MySQL database name
      database__connection__port: 3306           # MySQL port (default)

    # Volume mapping - persists Ghost data between container restarts
    # Maps named volume to Ghost's content directory inside container
    volumes:
      - ghost_content:/var/lib/ghost/content

    # Wait for MySQL to be ready before starting Ghost
    depends_on:
      - mysql-db

    # Put Ghost on custom network so it can talk to MySQL
    networks:
      - ghost-network

  # MySQL database container
  mysql-db:
    # MySQL version 8.0 Docker image
    image: mysql:8.0

    # Container name for easier identification
    container_name: ghost-mysql

    # Always restart if container stops/crashes
    restart: always

    # MySQL configuration through environment variables
    environment:
      # Root user password (administrative access)
      MYSQL_ROOT_PASSWORD: root_secure_password

      # Create a database specifically for Ghost
      MYSQL_DATABASE: ghost_db

      # Create a user specifically for Ghost (not root)
      MYSQL_USER: ghost_user

      # Password for the Ghost user
      MYSQL_PASSWORD: ghost_password

    # Volume mapping - persists MySQL data between container restarts
    # Maps named volume to MySQL's data directory inside container
    volumes:
      - mysql_data:/var/lib/mysql

    # Put MySQL on custom network so Ghost can connect to it
    networks:
      - ghost-network

# Named volumes - Docker manages these for persistent data storage
volumes:
  # Stores Ghost content (themes, images, uploads, etc.)
  ghost_content:
    # Docker creates and manages this volume
    driver: local

  # Stores MySQL database files
  mysql_data:
    # Docker creates and manages this volume
    driver: local

# Custom network - allows containers to communicate using container names
networks:
  ghost-network:
    # Bridge network type - containers can talk to each other
    driver: bridge

Now, I have used localhost in url but for making it available online I can use ngrok or custom domain but I was learning dockerization techniques and how to write dockerfile for a big project so I used localhost

We can change url later

  1. there are only 2 services we are using Ghost and MySql

  2. We have volumes to persist data storage

  3. And we have networks to allow containers to communicate

Now, for running it you can follow the process

  1. Save the file as docker-compose.yml

  2. Start everything:

     docker-compose up -d
    
  3. View logs:

     docker-compose logs -f ghost
    
  4. Access Ghost: Go to http://localhost:2368

  5. Stop everything:

     docker-compose down
    

    Migrating Existing Data:

    Since I already have Ghost with imported Beehiiv data, I can import my data with two options:

    Stop the docker and

    Option 1: Export/Import

    • Export from your current Ghost (Settings > Labs > Export)

    • Start this Docker setup

    • Import the JSON file into new Ghost

Option 2: Copy existing content

  • Find your current Ghost content folder

  • Copy it to Docker volume after first run

Then, you can recompose it


Part - III: Ends here → Mission Complete ✅

We have reached to the finish line for now 🏁

I would have hosted this and included that also but the thing is, it requires credit/debit card and my debit card of Rupay is not accepted so I didn’t explored this part I will add the kubernetes and hosted site blog later.

This is my first blog. So, “Pardon my mistakes”. Till then -

8 years of Ae Dil Hai Mushkil Album. Was Channa Merya the last iconic  heartbreak song? : r/BollywoodMusic

0
Subscribe to my newsletter

Read articles from Agrapujya Lashkari directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Agrapujya Lashkari
Agrapujya Lashkari