Ghost setup
Server setup | |
← Previous | Next → |
Discourse | Miscellaneous |
Ghost is also a bit opinionated. In particular, it expects to use Nginx as a web server, rather than Apache.
Installation
First, install Nginx. To allow Nginx to work on the same box as the Apache, change /etc/nginx/sites-enabled/default
to include
# listen 80 default_server; # listen [::]:80 default_server; listen 8080 default_server; listen [::]:8080 default_server;
I created an additional user for the installation, but I'm not sure that was necessary. Remember to use that user's password when it asks you for sudo
permission!
- Install the latest stable Node, following the standard instructions there.
- Install Ghost via the command-line interface, installing the blog in
/var/www/blog.domain.tld
.
When asked, specify the URL of the blog as https://blog.domain.tld
, noting that it's https
, not http
.
When asked, give server.domain.tld
as the hostname of the MySQL server, not the default localhost
.
I skipped SSL setup, instead later manually setting up Nginx to use the existing Lets Encrypt certificates by editing /etc/nginx/sites-enabled/blog.domain.tld
to include the lines:
ssl_certificate_key /etc/letsencrypt/live/blog.domain.tld/privkey.pem; ssl_certificate /etc/letsencrypt/live/blog.domain.tld/fullchain.pem;
I did try using a Docker-based installation of Ghost, but that didn't work well with passing through requests to generate the images in blog posts.
Installing additional blogs on the same server
This is much the same as with installing the first blog, but you must override the defaults for some settings.
Before installing a second blog on the same server, manually create the database and the database user:
user@server:~$ mysql -u root -p mysql> create database www_other_blog_org_prod; mysql> create user 'ghost_otherblog'@'localhost' identified by 'secretpassword'; grant all privileges on www_other_blog_org_prod.* to 'ghost_otherblog'@'localhost';
Then run the installation as normal:
ghostinstall@server:/var/www/otherblog$ ghost install
When prompted, give the database details you produced before, and ask Ghost not to create the ghost
database user.
Once you've done, take a look in /var/www/otherblog/config.production.json
for the port the local Ghost server is running on
"url": "https://www.otherblog.org", "server": { "port": 2369, "host": "127.0.0.1" },
Use that port number (2369
) in the Apache proxy settings below.
Apache proxy
You'll need an SSL certificate which covers this domain.
Create the Apache site file as /etc/apache2/sites-available/blog.domain.tld.conf
:
<VirtualHost *:80> ServerAdmin webmaster@localhost ServerName blog.domain.tld Redirect permanent / https://blog.domain.tld CustomLog /var/log/apache2/blog.domain.tld.access.log combined ServerSignature off </VirtualHost> <VirtualHost *:443> ServerAdmin webmaster@localhost SSLEngine On SSLCertificateKeyFile /etc/letsencrypt/live/blog.domain.tld/privkey.pem SSLCertificateFile /etc/letsencrypt/live/blog.domain.tld/fullchain.pem ServerName blog.domain.tld RequestHeader set X-Forwarded-Proto "https" ProxyPreserveHost On ProxyRequests Off ProxyPass / http://0.0.0.0:2368/ ProxyPassReverse / http://0.0.0.0:2368/ CustomLog /var/log/apache2/blog.domain.tld.access.log combined ServerSignature off </VirtualHost>
- Note the
X-Forwarded-Proto
request header.
Enable the site and reload the Apache configuration:
root@server:~# a2ensite blog.domain.tld root@server:~# systemctl reload apache2.service
Configuration
Visit https://blog.domain.tld/ghost
for final configuration.
Install the Ghost desktop app for easier control of the blog.
One thing that needs changing is the email service for transactional emails. The default "Direct" method doesn't seem to use any features such as SPF or DKIM, so those messages get blocked by the large email providers like Gmail. The solution is to use the existing Postfix_server_setup.
Edit the mail
section of Ghost's config.production.json
file to include the following:
"mail": { "from": "Ghost <ghost@blog-domain.tld", "transport": "SMTP", "options": { "service": "sendmail" } },
You'll also need to configure the DNS records for your blog's domain to include your mail server in the SPF record. In your blog domain's DNS record, include a TXT
field with contents v=spf1 mx a mx:domain.tld ~all
Install a theme
Several of the changes below will require changes to the theme, so it's a good idea to get the theme sorted out now. I use a variant on the Willow theme (my fork on Github).
- Clone the theme, from the Github source, to your desktop machine.
- In the theme directory, install Grunt and get it watching the file change. (This will re-create the
assets/css/styles.css
when there are changes in the files its derived from.
user@desktop:~/blog/theme$ npm install user@desktop:~/blog/theme$ grunt
- Alter
package.json
to reflect the changes you're about to make.
- Alter
partials/sidebar.hbs
to adjust the sidebar links. (Look on Github, as Mediawiki doesn't like paired curly brackets in markup.)
- Adjust
source/sass/components/_post-view.scss
to allow floating images. In the "Post view" section near the top of the file, add these three lines:
img[src$="#left"] { max-width:30%; float: left; } img[src$="#right"] { max-width:30%; float: right; } img[src$="#full"] { max-width:none;width:100vw }
- You should see the Grunt watcher rebuild the style sheet now.
- Move to the directory above and zip the theme:
user@desktop:~/blog$ zip -x '*/node_modules/*' '*/.git/*' -r ghost-theme-willow.zip theme
- Use the Ghost desktop app to upload the theme. If you change styles, you'll also need to restart the Ghost server. Changes to
.hbs
files take effect immediately.
- Commit your changes into Git, as you would for any other project.
- Add some images to your blog.
- Publication icon is the favicon, and should be 60×60 pixels.
- Publication logo is a small symbol used in the sidebar header, about 135×135 pixels.
- Publication cover is a large background image used in the sidebar. Mine's a 550×250 pixel Jpeg.
You can now add floating images with by appending #left
, #right
, or #full
to the image URL when creating a post.
Install Disqus
The standard instructions are good. Remember to:
- Note the Disqus site shortname.
- Add
blog.domain.tld
to Disqus as a trusted domain - Disable the "tracking" and "affiliated sites" in Disqus advanced settings
- Update the theme's
post.hbs
file to include the Disqus code. (Look on Github, as Mediawiki doesn't like paired curly brackets in markup.)
- Note that you should use you own site's Disqus shortname.
- Again, zip and upload the modified theme.
Install MathJax and Prism
MathJax renders Latex formulae. Prism does syntax highlighting. It can all be done by code injection.
- In the Code Injection tab of the desktop app, add these lines to the blog header:
<link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/themes/prism.min.css" rel="stylesheet"/> <link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet"/> <link href="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet"/>
- and add these lines to the blog footer:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/prism.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/toolbar/prism-toolbar.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/show-language/prism-show-language.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/plugins/line-numbers/prism-line-numbers.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-clike.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-javascript.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-latex.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-markdown.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-haskell.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-python.min.js"></script> <script type="text/javascript" async src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"> MathJax.Hub.Config({ tex2jax: { inlineMath: [["$", "$"], ["\\(", "\\)"]], processEscapes: true } }); </script>
Add additional Prism lines to the footer for additional languages.
Prism markup examples
When adding code blocks to posts, use this formatting:
```python def tpack(text, width=100): """Pack a list of words into lines, so long as each line (including intervening spaces) is no longer than _width_""" lines = [text[0]] for word in text[1:]: if len(lines[-1]) + 1 + len(word) <= width: lines[-1] += (' ' + word) else: lines += [word] return lines ```
If you want line numbers:
<pre><code class="language-python line-numbers">def tpack(text, width=100): """Pack a list of words into lines, so long as each line (including intervening spaces) is no longer than _width_""" lines = [text[0]] for word in text[1:]: if len(lines[-1]) + 1 + len(word) <= width: lines[-1] += (' ' + word) else: lines += [word] return lines </code></pre>
LaTeX markup examples
For inline equations, use \\(
and \\)
to delimit the equation, so
And then some more text \\(\alpha \in \Gamma\\) to finish
will render as
- And then some more text to finish
For block equations, use either $$
or \\[
/ \\]
to delimit the blocks, so
$$\prod_{\mathfrak{p} \in \Omega}\(\frac{\alpha,-1}{\mathfrak{p}})=1$$ and \\[(X,\beta) \oplus (X,-\beta) \text{ is split }.\\]
will render as:
and
Install ghostHunter search
Ghosthunter is a client-side search engine for Ghost blogs.
The instructions on the ghosthunter homepage are pretty good for covering the basics. The notes from Haunted Themes are a good supplement. However, I had to do a couple of other things.
1. Install the Content API package, so that ghosthunter can see the blog's content. (I think this shouldn't be necessary, but I seemed to require it.)
2. Add the site's host name to /assets/js/main.js, in the content-api-host
field in the definition of the config
object.
3. Change the prettyPubDate
to pubDate
in all the places it occurs, including files in the assets/ghosthunter/dist
directory.
Startup scripts
The blog engine needs to start up when the system boots.
Create the file /lib/systemd/system/ghost_blog_domain_tld.service
containing
[Unit] Description=Ghost systemd service for blog: blog.domain.tld Documentation=https://ghost.org/docs/ After=network-online.target remote-fs.target nss-lookup.target [Service] Type=simple WorkingDirectory=/var/www/blog.domain.tld User=999 Environment="NODE_ENV=production" ExecStart=/usr/bin/node /usr/bin/ghost run Restart=always [Install] WantedBy=multi-user.target
Ensure that SystemD knows about the new service:
root@server:~# systemctl daemon-reload
Start the service:
root@server:~#:systemctl start ghost_blog_domain_tld.service
That should be enough for the blog engine to start up on each boot.