A self-hosted Ghost blog needs less than people expect: about 1 GB of memory for a personal site, a domain with HTTPS in front of it, the url setting matching that domain exactly, and working outgoing mail. Those last two are where nearly every broken Ghost install goes wrong. Get the address and the mail right on day one, keep a copy of the content folder somewhere other than the server, and the rest - themes, members, newsletters, updates - is ordinary work in the admin screen.
This guide covers what Ghost actually consists of on a server, how big a server it needs, the handful of settings that matter, the difference between the two kinds of email Ghost sends, themes, backups and updates, and the errors people hit most often.
What a self-hosted Ghost install consists of#
Ghost is a Node.js application. Everything you see - the public site, the members portal, the editor at /ghost - is served by one long-running process, which listens on a single port. By default that port is 2368. Nothing about Ghost needs PHP, a separate web server or a cron job; it is one process plus its files.
Those files fall into two groups, and the split is the most useful thing to understand before you touch anything:
- The application - Ghost's own code and its Node.js dependencies. It is replaced on every update, and you should never edit it.
- The `content` folder - everything that is yours. Your themes in
content/themes, uploaded images incontent/images, the routing file incontent/settings, logs incontent/logs, and the database incontent/data.
Plus one configuration file, config.production.json, which tells Ghost its public address, where its database is, how to send mail and which address and port to listen on.
The database is the third piece. Ghost can use MySQL or SQLite. Ghost's own documentation recommends MySQL 8 for production and treats SQLite as the lightweight option, but SQLite is a single file inside content/data, which makes a small publication very easy to run and to back up: there is no separate server to size, secure or pay for. For a blog with one or a handful of writers, it is a reasonable choice. Where it stops being reasonable is covered in the FAQ.
On RE:NODE, Ghost is one of the one-click app lines. The server installs and builds Ghost on its first start and keeps its data in SQLite on the server, so there is no database to buy. Every file the installer writes is visible in the file manager and over SFTP, the same as on any other server, and the changes you make to themes and configuration are not overwritten by an update.
How big a server Ghost needs#
Ghost's own minimum is 1 GB of memory. That is honest: an idle Ghost process sits comfortably below that, and a personal blog with a few thousand visits a day runs on it without trouble. What pushes memory and CPU up is not page views - Ghost caches well and most pages are cheap - but these:
- Image uploads. Ghost generates resized copies of every image for responsive layouts. Uploading twenty large photographs in a row is the heaviest thing a small Ghost server does.
- Members and newsletters. Sending a newsletter to a large list, importing a members CSV and rendering the members area all cost more than serving posts.
- Building on first start. Installing dependencies and building takes more memory than running. It happens once.
Disk is mostly images. The application itself is a few hundred megabytes; text posts are tiny; a photography blog grows by the size of its uploads plus the resized copies Ghost makes of them, so budget roughly twice the size of what you upload.
| Plan | RAM / disk / CPU | Suits |
|---|---|---|
| Ghost Starter | 1 GB / 10 GB / 1 vCPU | A personal blog or small newsletter |
| Ghost Standard | 2 GB / 20 GB / 1.5 vCPU | A growing publication with members |
| Ghost Advanced | 4 GB / 30 GB / 2 vCPU | Busy sites and large newsletter lists |
| Ghost Pro | 6 GB / 50 GB / 3 vCPU | Large publications with heavy media |
Those are the RE:NODE Ghost plans, starting from $5 a month. Start small: moving up a tier changes the limits on the server you already have, and Ghost does not need to be reinstalled. If you want the general reasoning about which resource actually runs out first, Node memory limits explained applies directly, because Ghost is a Node process like any other.
First start and the admin account#
On a self-managed machine, Ghost is normally installed with Ghost-CLI: ghost install on a prepared Linux server sets up Node.js dependencies, a MySQL database, nginx, a certificate and a systemd service. It is a perfectly good tool, but it assumes you own the whole machine, which is why it fits a VDS rather than a panel server.
On a one-click line the steps are shorter:
- Order the plan. The server is created when payment clears, usually inside a minute.
- Start it and watch the console. The first start downloads and builds Ghost, which takes longer than any later start. Wait until Ghost reports that it is running.
- Open the address shown in the panel with
/ghoston the end. The first visit to/ghostis the setup screen: site title, your name, email and password. That account becomes the owner. - Set up the domain (next section) before you invite anyone or publish anything, because links and emails use whatever address Ghost believes it has.
Do the setup step promptly. Until an owner exists, whoever reaches /ghost first creates it. On a fresh server nobody knows the address yet, but it is not a step to leave for next week.
Staff accounts come next: Ghost has Contributor, Author, Editor and Administrator roles, and there is exactly one Owner. Invite writers by email from the staff settings - which means mail needs to work first, and is the reason the mail section below is not optional.
The url setting and your domain#
url is the setting that breaks more Ghost sites than any other. Ghost builds every link, canonical tag, RSS item, sitemap entry, image address and email link from it, and it redirects visitors to it. It must be the exact public address, scheme included:
{ "url": "https://blog.example.com", "server": { "host": "0.0.0.0", "port": 2368 }}The same values can come from environment variables instead of the file. Ghost reads nested keys with a double underscore, so server__port sets server.port and mail__transport sets mail.transport:
url=https://blog.example.comserver__host=0.0.0.0server__port=2368An environment variable wins over the file. That is worth remembering when an edit to config.production.json seems to be ignored: something else is setting the same key. On a panel server, the address and port Ghost listens on are already arranged by the install to match the port allocated to the server, so leave server.host and server.port alone unless you know why you are changing them. url is the one you change.
On RE:NODE, app plans include a reverse-proxy slot. Add your domain in the proxy slot, point an A record for it at the address the panel shows, and the certificate is issued and renewed automatically. Then set url to https:// plus that domain and restart Ghost. The order matters: if you set url to the domain before DNS and the certificate are in place, Ghost redirects every visitor, including you, to an address that does not answer yet.
The domain side - A records, how long DNS takes and what the certificate needs - is in your domain and its certificate, and what a reverse proxy does explains why Ghost itself never handles HTTPS here. One consequence of the proxy: Ghost sees every visitor arriving from the proxy, and the real client address arrives in the X-Forwarded-For header.
If you want both example.com and www.example.com to work, pick one as url and redirect the other to it; Ghost answers on one canonical address. www vs apex domain and redirects covers which to choose.
Email: transactional mail and newsletters#
Ghost sends two different kinds of email through two different paths, and confusing them is the commonest reason a new site feels half broken.
Transactional mail is one message to one person: staff invitations, password resets, and - the one that matters most - the sign-in links members get. Ghost members do not have passwords; they sign in through a link sent by email. If transactional mail does not work, nobody can sign up or sign in, and the portal fails quietly from the reader's side. This mail goes out through the mail block in the configuration, usually over SMTP:
{ "mail": { "from": "'Example Blog' <noreply@example.com>", "transport": "SMTP", "options": { "host": "smtp.example.com", "port": 465, "secure": true, "auth": { "user": "noreply@example.com", "pass": "your-smtp-password" } } }}| Key | Typical value | What it does |
|---|---|---|
mail.transport | SMTP | Send through an SMTP server |
mail.from | your address | The sender readers see |
mail.options.host | provider host | The SMTP server to connect to |
mail.options.port | 465 or 587 | 465 with secure: true, 587 without |
mail.options.auth | user and pass | The SMTP login |
Port 465 uses TLS from the first byte and wants secure: true; port 587 starts plain and upgrades, and wants secure left false. Mixing them up produces a timeout or a TLS error rather than a helpful message. The from address has to be one the SMTP server is allowed to send as, on a domain whose SPF and DKIM records include that server, or the mail lands in spam - why email goes to spam goes through the records.
Where the SMTP server comes from is your choice: a transactional mail provider, the outgoing server of an existing mailbox, or your own. RE:NODE sells a Mail Server line running Stalwart, which can act as the SMTP server for Ghost - Stalwart mail server setup covers it, and transactional email from your app compares the options. Ghost only needs to reach a submission port such as 587 or 465; it never needs port 25.
Newsletters are the bulk path: one post emailed to every subscriber. Ghost does not send newsletters over SMTP. Bulk sending goes through Mailgun, configured in Ghost Admin under the newsletter email settings with a Mailgun domain and API key, not in config.production.json. Without it, Ghost still publishes posts and still lets members sign up, but the option to email a post is not available. A blog with no newsletter can skip Mailgun entirely; a blog whose whole point is the newsletter cannot.
Themes and customisation#
A Ghost theme is a folder of Handlebars templates (.hbs files) with CSS, JavaScript and a package.json that describes the theme and its settings. Recent versions ship a default theme, and the official and marketplace themes are free or paid zip files.
There are two ways to install one:
- Upload the zip in Ghost Admin, under the design settings. Ghost validates it, lists any errors or warnings, and lets you activate it. This is the normal route.
- Copy the folder into `content/themes` over SFTP or the file manager, then restart Ghost so it notices the new theme, and activate it in the admin. Useful for a theme you are developing, or one too large to upload comfortably.
Edit themes as a copy, never in place. Rename the folder (and the name in its package.json) so a theme update from its author cannot silently replace your changes, and keep your modified theme in Git or at least as a zip on your own machine. SFTP and the file manager has the connection details for working on the files directly.
Two other files under content are worth knowing:
content/settings/routes.yamlcontrols URL structure: custom collections, a static home page, taxonomies. It can be downloaded and uploaded from the admin. A bad routes file can make whole sections of the site return 404, so keep the previous version before you change it.- Code injection in the admin adds snippets to the site head and footer - analytics, a font, a small style fix - without touching the theme. Prefer it for small changes; they survive a theme switch.
Large theme changes are easier to test somewhere other than the live site. A second, small Ghost server for staging costs little and is covered in running a test server beside production.
Backups that include everything#
A Ghost site is three things: the database, the content folder, and config.production.json. A backup that misses any one of them is not a complete backup.
With SQLite, the database lives inside content/data, which makes a full backup a copy of content plus the config file. The catch is consistency. Copying a SQLite file while Ghost is writing to it can produce a copy that will not open. The safe options, in order of preference:
- Stop Ghost, copy the files, start it again. A few seconds of downtime, a guaranteed clean copy.
- Use SQLite's own backup command if the
sqlite3tool is available, which produces a consistent copy of a live database. - Take the copy at a quiet hour and keep several, so one bad copy is not your only one.
$ sqlite3 content/data/ghost.db ".backup 'ghost-backup.db'"$ tar czf ghost-content-$(date +%F).tar.gz content config.production.jsonThe file name ghost.db is the usual one, but check what is actually in content/data before you script anything.
Ghost also has an export in its settings that downloads posts, pages, tags and settings as a JSON file, and members can be exported as CSV. These are useful - the JSON export is how you move content between Ghost installs of different versions - but they are not backups on their own. The JSON export does not include images or themes, and restoring a whole site from it is slower and lossier than restoring files.
On RE:NODE, every Ghost plan has backup slots: from one on Starter to four on Pro. A backup can be taken on demand or on a schedule, is stored off the machine it protects, can be downloaded, and is restored with a button. The Schedules tab can stop the server, take the backup and start it again in one ordered task, which is the stop-copy-start pattern above without anyone awake for it. Lock the backup you take before an update so rotation cannot remove it. Deleting a server deletes its backups too, locked ones included, so keep a downloaded copy of anything you would hate to lose. Backups that actually restore explains why you should restore one now and then, and the panel steps are in the backups guide.
Updating Ghost safely#
Ghost releases often. Minor updates are routinely painless; major versions change things that themes and integrations depend on, and the release notes say what. On a machine you manage with Ghost-CLI, ghost update downloads the new version, runs database migrations and restarts. How a version moves forward on a one-click server depends on its install; check the console output on start, and ask support before a major upgrade if you are not sure what will happen.
Whatever does the update, the routine is the same:
- Read the release notes for every major version between yours and the target.
- Check your theme against the new version. Ghost's theme validator (the same one that runs on upload) reports templates and helpers that are no longer supported. A theme that worked on the old major version may refuse to activate on the new one.
- Back up, with Ghost stopped, and lock that backup.
- Update, then watch the log while Ghost runs its database migrations. Do not stop the server in the middle of a migration.
- Click through the site: home page, a post, a tag page, the members portal, the admin editor. Send yourself a test email.
If an update goes wrong, restoring the backup returns the files and the database to exactly the state before it - which is why the backup comes before the update, not after the first error. The general habits for this are in zero-downtime deploys on a small server, even though a single Ghost process will have a short restart either way.
Troubleshooting#
The site redirects in a loop, or keeps sending you to the wrong address. url does not match how the site is reached. Check the scheme (https versus http), the host name (www or not) and that no trailing path was added. Behind a reverse proxy, Ghost also relies on the proxy telling it the original request was HTTPS through the X-Forwarded-Proto header; if that is missing, an https url causes an endless redirect.
Members never receive the sign-in link. Transactional mail is not configured or is failing. Look in the Ghost log for the SMTP error. Common causes: the wrong port and secure combination, an SMTP login that is not allowed to send as the from address, or the mail arriving but being filed as spam because SPF or DKIM is missing.
The option to send a post by email is missing. Newsletters need Mailgun configured in the admin. SMTP settings do not enable bulk sending.
Images or links point to the old address after a domain change. Change url, restart, and the generated links follow. Images inserted into posts before the change are stored with paths Ghost rewrites from url, but anything hard-coded in a theme or in code injection has to be edited by hand.
A theme upload fails with errors. The validator found templates or helpers that the running Ghost version does not support. Read the listed errors; they name the file. Fix them in your copy of the theme, or get the version of the theme built for your Ghost release.
Ghost restarts during heavy uploads or imports. The process reached its memory limit. Upload fewer images at once, resize photographs before uploading, or move up a tier. A restart loop that does not stop is covered in why your game server keeps restarting - the diagnosis is the same for any process.
An edit to `config.production.json` changes nothing. Either Ghost was not restarted (it reads the file only on start) or an environment variable is setting the same key and winning.
FAQ#
Is SQLite good enough for a Ghost blog?
For a personal blog or a small publication, yes - it is fast, needs no separate server and backs up as a file. Ghost's own guidance prefers MySQL 8 for production, and a site with many writers, a very large members list or heavy concurrent writes is where MySQL starts to earn its cost. Moving later is a content export and import into a new install configured for MySQL.
Do I need Mailgun to run Ghost?
Only for newsletters. Sign-in links, staff invitations and password resets go through ordinary SMTP. A blog that never emails its posts to subscribers can run without Mailgun indefinitely.
Can I use my own domain with free HTTPS?
Yes. Add the domain in the proxy slot, point an A record at the server's address, wait for the certificate, then set url to the https address and restart Ghost. The certificate is renewed automatically.
How much memory does a Ghost blog need?
About 1 GB for a personal blog, which is Ghost's own stated minimum. More helps with large image uploads, big member imports and newsletter sends. Page views alone rarely justify a bigger plan.
Will an update overwrite my theme changes?
A Ghost update replaces the application, not the content folder, so your themes and images stay. The risk is a theme update from its author overwriting your edits, which is why you rename a modified theme. Back up before every update regardless.
Can I move an existing Ghost site to a new server?
Yes. Export the content as JSON from the old site, copy the content/images folder and your theme, then import the JSON into the new install and upload the theme. Members export and import as CSV. Copying the whole content folder works too, as long as both installs run the same Ghost version.


