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-Protorequest 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.csswhen there are changes in the files its derived from.
user@desktop:~/blog/theme$ npm install user@desktop:~/blog/theme$ grunt
- Alter
package.jsonto reflect the changes you're about to make.
- Alter
partials/sidebar.hbsto adjust the sidebar links. (Look on Github, as Mediawiki doesn't like paired curly brackets in markup.)
- Adjust
source/sass/components/_post-view.scssto 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
.hbsfiles 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.tldto Disqus as a trusted domain - Disable the "tracking" and "affiliated sites" in Disqus advanced settings
- Update the theme's
post.hbsfile 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.