> ## Content Index
> Fetch the complete content index at: https://wealthlab.life/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Deploy Ghost on DigitalOcean with Docker Compose
- URL: https://wealthlab.life/deploy-ghost-digitalocean-docker/
- Published: 2026-09-03T00:07:05.000Z
- Updated: 2026-09-03T00:07:05.000Z
- Description: Deploy Ghost on a DigitalOcean Droplet with Docker Compose, Caddy, MySQL, Cloud Firewall rules, backups, and production checks.
- Author: WealthLab
- Tags: DigitalOcean, Ghost, Docker, VPS

*Disclosure: WealthLab.life may earn a commission when you register through some links on this page, at no additional cost to you.*

You can deploy Ghost on DigitalOcean by creating an Ubuntu Droplet, securing access with SSH keys and a Cloud Firewall, installing Docker from its official repository, and running Ghost's official Docker Compose stack behind Caddy. For a small publication, 2 GB RAM is a sensible starting point; choose more headroom for imports, traffic spikes, analytics, or additional services.

## Quick answer and architecture

This guide uses the official Ghost Docker tooling rather than an improvised single-container command. The stack includes Ghost, MySQL, and Caddy. Caddy terminates HTTPS, MySQL stores publication data, and a bind-mounted Ghost content directory keeps images, themes, and other persistent files outside the application container.

- **DNS:** an A record sends the publication hostname to the Droplet.
- **Public ports:** only 80 and 443 serve the site; SSH should be restricted where practical.
- **Application:** Ghost is reached through Caddy, not exposed directly to the internet.
- **Data:** MySQL and Ghost content require separate, tested backups.
- **Email:** a real SMTP provider is required for password resets and transactional mail.

If you want a provider-neutral walkthrough first, read [How to Install Ghost with Docker on a VPS](https://wealthlab.life/install-ghost-docker-vps/). This page concentrates on DigitalOcean-specific provisioning, firewall, recovery, and backup decisions.

## Requirements

- A domain or subdomain you control.
- A DigitalOcean account and an uploaded SSH public key.
- A current Ubuntu LTS image supported by Docker.
- At least 2 GB RAM for a practical small production starting point.
- An SMTP account for Ghost transactional email.
- An off-server destination for database and content backups.

One gigabyte may run a light test, but it leaves limited room for MySQL, image pulls, upgrades, and temporary memory spikes. Review the [Ghost VPS sizing guide](https://wealthlab.life/ghost-vps-sizing/) before choosing a Droplet.

## 1\. Create the DigitalOcean Droplet

In the DigitalOcean control panel, choose **Create → Droplet**. Select a region near most readers, then verify that the exact Droplet type and any dependent products are available there. Choose a plain Ubuntu LTS image so the installation remains understandable and reproducible.

Use an SSH key instead of password-only access. Enable monitoring if it fits your operating plan, and decide whether to enable DigitalOcean backups at creation. Platform backups are useful for whole-server recovery, but they do not replace portable MySQL exports and Ghost content archives.

Add a clear tag such as `ghost-production`. Tags make it easier to attach a Cloud Firewall consistently and distinguish production resources from experiments.

If you need a server for this deployment, [view DigitalOcean through WealthLab's referral link](https://m.do.co/c/a3b3fb312857). Confirm the current plan specification, regional availability, backup options, and final price before creating anything.

## 2\. Create a Cloud Firewall before launch

DigitalOcean Cloud Firewalls are stateful and block traffic not explicitly permitted by an inbound rule. Attach the firewall to the `ghost-production` tag, then allow:

- TCP 80 from all IPv4 and IPv6 addresses for HTTP validation and redirect traffic;
- TCP 443 from all IPv4 and IPv6 addresses for the publication;
- TCP 22 only from your trusted administration address or network when possible.

Do not expose MySQL port 3306 or Ghost port 2368 publicly. Keep normal outbound traffic allowed so the server can retrieve packages, container images, certificates, and email or API connections.

A provider firewall is not permission to publish every Docker port. Docker creates packet-filtering rules for published ports, and Docker's documentation warns that exposed container ports can bypass some host firewall expectations. Publish only Caddy's web ports in Compose and verify exposure with an external port scan you control.

## 3\. Connect and patch Ubuntu

```bash
ssh root@YOUR_DROPLET_IP
apt update
apt upgrade -y
reboot
```

Reconnect after the reboot. For ongoing operation, create a named administrator with sudo access, copy the SSH key, verify a second login session, and only then consider disabling direct root or password login. Keep the DigitalOcean recovery console path documented before tightening SSH.

## 4\. Point DNS to the Droplet

Create an A record for the exact hostname, such as `blog.example.com`, using the Droplet's public IPv4 address. Add an AAAA record only if IPv6 is enabled and tested end to end. Confirm resolution before starting Caddy:

```bash
getent ahosts blog.example.com
```

DNS must resolve publicly and ports 80/443 must reach the Droplet for automatic certificate issuance. If Cloudflare or another proxy is in front, start with a configuration you understand and verify the origin can still complete certificate and redirect flows.

## 5\. Install Docker from the official repository

Docker recommends its apt repository for normal Ubuntu installations and upgrades. Avoid blindly piping a remote convenience script into a privileged shell. Follow Docker's current Ubuntu instructions to install Docker Engine, the CLI, containerd, Buildx, and the Compose plugin, then verify:

```bash
docker --version
docker compose version
systemctl is-active docker
```

Adding a user to the `docker` group grants powerful host-level access. Limit membership and treat Docker access as administrative access.

## 6\. Install the official Ghost Docker tooling

Ghost 6's self-hosting documentation provides an official Compose project. Clone it into `/opt/ghost`, then create local configuration files:

```bash
git clone https://github.com/TryGhost/ghost-docker.git /opt/ghost
cd /opt/ghost
cp .env.example .env
cp caddy/Caddyfile.example caddy/Caddyfile
chmod 600 .env
```

Review the repository before execution. Pinning or recording the deployed commit makes future changes auditable.

## 7\. Configure Ghost secrets, domain, and SMTP

Edit `/opt/ghost/.env`. Set `DOMAIN` to the public hostname without a URL path. Generate different random values for the database root password and Ghost database password:

```bash
openssl rand -hex 32
openssl rand -hex 32
```

Configure SMTP using your provider's documented host, port, security mode, username, password, and From address. Ghost needs working transactional email for account invitations and password resets. Never commit `.env`, paste it into tickets, or include it in an unencrypted archive.

If you use a separate admin hostname or redirect between root and `www`, select the matching documented Caddy block. Keep one canonical HTTPS hostname and make DNS match it.

## 8\. Validate and start the stack

```bash
cd /opt/ghost
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
```

`docker compose config --quiet` catches interpolation and syntax problems, but rendered output can reveal secret values, so do not paste the full result publicly. After startup, inspect bounded logs rather than assuming an “Up” container is healthy:

```bash
docker compose logs --tail=100 caddy ghost db
```

Visit the public HTTPS URL, then open `https://YOUR_DOMAIN/ghost/` to complete owner setup. Confirm there is no redirect loop and that Ghost generates canonical links with HTTPS.

## 9\. Run a production verification pass

- The homepage and a test post return HTTP 200 over HTTPS.
- HTTP redirects to the canonical HTTPS hostname.
- `/ghost/` loads without exposing the database or Ghost container port.
- `/robots.txt` and `/sitemap.xml` return 200.
- Only intended ports are reachable publicly.
- A password-reset or invitation email can be delivered.
- Containers restart after a controlled host reboot.
- Disk, memory, and swap pressure are monitored.

Use the [Docker VPS production checklist](https://wealthlab.life/docker-vps-checklist/) for the broader launch gate.

## 10\. Build backups around restoration

DigitalOcean backups are whole-Droplet disk images. They can help rebuild or restore a server, but keep an independent application-aware backup too:

- export MySQL consistently;
- archive the Ghost content directory and deployment configuration;
- store a protected copy outside the Droplet;
- record image versions and DNS settings;
- restore into a disposable server and verify posts, images, members, and themes.

Do not treat a successful backup command as proof of recovery. The meaningful test is a completed restore. Schedule backups before upgrades and retain enough history to recover from a problem discovered late.

## 11\. Update Ghost safely

Before an update, read release notes, take application-consistent backups, record current image identifiers, validate Compose, and define rollback. Pull and recreate only after the backup exists:

```bash
cd /opt/ghost
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 ghost
```

Then verify the homepage, Admin, a post with images, SMTP, robots, sitemap, and the next scheduled backup. Avoid unattended major-version jumps.

## Troubleshooting

### Caddy cannot issue a certificate

Check public DNS, Cloud Firewall rules, host-level filtering, and whether another process occupies ports 80 or 443\. Confirm the hostname in `.env` exactly matches DNS.

### The site redirects repeatedly

Verify Ghost's configured URL is the canonical HTTPS URL. If an external proxy terminates TLS, it must send the correct forwarded scheme and host. Do not add multiple competing redirect layers.

### Ghost or MySQL keeps restarting

Inspect bounded logs, then check free memory, swap, disk capacity, permissions, and database health. A tiny Droplet may fail during image pulls, migrations, or database maintenance even if idle use looked acceptable.

### SMTP login works but messages do not arrive

Authentication alone does not prove delivery. Check Ghost logs, the provider's message events, verified sending domain, DNS authentication, From address, suppression lists, and the recipient's spam folder.

### The Cloud Firewall is correct but a port is still public

Recheck the firewall is attached to the intended Droplet or tag, inspect `docker compose ps`, and list listening sockets. Test from outside the Droplet. Docker port publishing and host firewall behavior must be evaluated together.

## When this setup is not a good fit

A self-managed DigitalOcean Droplet is a poor fit when nobody can own Linux updates, database recovery, security monitoring, and incidents. Ghost(Pro) or another managed platform may be better when operations time costs more than infrastructure control. Also pause when you need compliance or support commitments that have not been verified for the selected architecture.

## FAQ

### What size DigitalOcean Droplet should I use for Ghost?

For a small publication, 2 GB RAM is a practical starting point. Use 4 GB or more when imports, background work, traffic, analytics, or co-located services require headroom. Measure actual memory, CPU, and database behavior.

### Should I use the DigitalOcean Ghost Marketplace image?

A Marketplace image can be convenient, but this guide uses the official Ghost Docker project so the stack and update path are explicit. Whichever path you choose, verify its current maintenance, architecture, backup procedure, and upgrade instructions.

### Do I need both a Cloud Firewall and Caddy?

Yes, they solve different problems. The Cloud Firewall filters network traffic before it reaches the Droplet; Caddy handles HTTP routing and TLS. Neither replaces secure SSH, updates, restricted container ports, or backups.

### Are DigitalOcean backups enough?

No. Use them as one recovery layer, then keep portable MySQL and Ghost-content backups outside the Droplet and test restoring them.

### Can I move this Ghost site to another provider later?

Yes, if you keep configuration, database exports, content, DNS notes, and secrets organized independently of the VM. Test the restore on a new server before changing DNS.

## Related WealthLab guides

- [Install Ghost with Docker on any VPS](https://wealthlab.life/install-ghost-docker-vps/)
- [Ghost VPS RAM, CPU, and storage requirements](https://wealthlab.life/ghost-vps-sizing/)
- [Best VPS for Ghost hosting: provider-fit guide](https://wealthlab.life/best-vps-ghost-hosting/)
- [Set up a DigitalOcean Droplet for Docker apps](https://wealthlab.life/digitalocean-droplet-docker-checklist/)
- [DigitalOcean vs Vultr](https://wealthlab.life/digitalocean-vs-vultr/)
- [Docker VPS production checklist](https://wealthlab.life/docker-vps-checklist/)

## Sources checked

- [Ghost's official Docker installation documentation](https://docs.ghost.org/install/docker)
- [DigitalOcean Droplet creation documentation](https://docs.digitalocean.com/products/droplets/how-to/create/)
- [DigitalOcean production-ready Droplet setup](https://docs.digitalocean.com/products/droplets/getting-started/recommended-droplet-setup/)
- [DigitalOcean Cloud Firewall documentation](https://docs.digitalocean.com/products/networking/firewalls/how-to/create/)
- [DigitalOcean backups documentation](https://docs.digitalocean.com/products/backups/)
- [Docker Engine installation on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
- [Docker packet filtering and firewall guidance](https://docs.docker.com/engine/network/packet-filtering-firewalls/)

*Official sources reviewed September 3, 2026\. Product availability, requirements, and interfaces can change; verify the current documentation before deployment.*