Home / Alt manpages / hugo-import-jekyll(1)

  • hugo-import-jekyll(1)
  • User command
  • linux

Import a Jekyll Site into Hugo Without Overwriting Work

An import that reports success can still leave a half-converted site, so this checks hugo import jekyll at every step instead of trusting the summary. You will import an existing Jekyll site into a separate Hugo target, then inspect exactly what got generated before using any of it. The examples use Hugo 0.123.7, installed from the Ubuntu hugo package. Allow about fifteen minutes for a small site, plus review time. The import changes files in the target directory, so keep the Jekyll source and a backup of any existing Hugo work.

This follows the installed command's documented contract: hugo import jekyll JEKYLL_ROOT TARGET. Both paths are required. It accepts --force when you deliberately need to import into a non-empty target.

1. Check the installed command

First confirm the binary and version. Both are ordinary read-only commands and need no elevated privileges:

$ command -v hugo
/usr/bin/hugo
$ hugo version
hugo v0.123.7+extended linux/amd64 BuildDate=2026-03-17T19:51:14Z VendorInfo=ubuntu:0.123.7-1ubuntu0.3+esm2
$ hugo import jekyll --help
Import from Jekyll requires two paths, e.g. hugo import jekyll jekyll_root_path target_path.

The exact help output can include extra inherited options. What actually matters: jekyll is the subcommand, followed by the source root and the target path. This is an import, not a substitute for a general Hugo build command.

2. Prepare a clean target

Choose an explicit source directory and a new target, replacing the example paths with directories you control:

$ JEKYLL_ROOT=/srv/sites/example-jekyll
$ HUGO_TARGET=/srv/sites/example-hugo-import
$ test -d "$JEKYLL_ROOT" && echo "source directory exists"
$ test ! -e "$HUGO_TARGET" && echo "target path is unused"
source directory exists
target path is unused

Checkpoint

The first test should succeed and the second should print target path is unused. If the target already exists, stop and inspect it. The normal command refuses a non-empty target anyway, but deciding what belongs there before asking Hugo to write is safer still.

You need read access to the Jekyll source and write access to the target's parent directory. Run as your normal account where possible; use sudo only if the chosen paths genuinely require it, because elevated privileges can leave root-owned output your ordinary account cannot later edit.

3. Confirm the Jekyll root has importable content

Look for the content that makes this a Jekyll root, without modifying anything:

$ find "$JEKYLL_ROOT" -maxdepth 2 -type d \( -name _posts -o -name _drafts \) -print
/srv/sites/example-jekyll/_posts
$ find "$JEKYLL_ROOT/_posts" -maxdepth 1 -type f -print | head
/srv/sites/example-jekyll/_posts/2026-09-20-first-post.md

This is a checkpoint, not a reason to create empty directories just to pass it. On this installed version, an empty root fails with abort: jekyll root contains neither posts nor drafts and exits with status 1. If neither _posts nor _drafts exists or holds the material you expect, fix the source selection rather than forcing the import through.

4. Run the import into the clean target

With the source and target checked, run the import:

$ hugo import jekyll "$JEKYLL_ROOT" "$HUGO_TARGET"
Import Jekyll from: /srv/sites/example-jekyll to: /srv/sites/example-hugo-import

A zero exit status means the command completed. It does not prove every post, draft, link or template matches the Hugo site you actually wanted. Hugo writes the imported result under the target path; do not treat that target as disposable until you have checked it.

Verify the target now has files and that the source is still there:

$ test -d "$HUGO_TARGET" && echo "target exists"
$ find "$HUGO_TARGET" -type f | head
$ test -f "$JEKYLL_ROOT/_posts/2026-09-20-first-post.md" && echo "source still exists"
target exists
/srv/sites/example-hugo-import/config/_default/hugo.yaml
/srv/sites/example-hugo-import/content/posts/first-post.md
source still exists

File names and generated configuration will vary with the source site. The checks that matter are that the target exists, holds the imported files, and the original Jekyll tree has not been touched.

5. Review before building or replacing anything

Inspect the target as an ordinary directory:

$ find "$HUGO_TARGET" -maxdepth 2 -type f -print | sort
$ git -C "$HUGO_TARGET" status --short 2>/dev/null || true
$ hugo --source "$HUGO_TARGET" --destination "$HUGO_TARGET/public" --quiet
$ test -f "$HUGO_TARGET/public/index.html" && echo "site build produced index.html"
site build produced index.html

That last command is a separate Hugo build and can expose missing themes, templates or configuration the import itself cannot diagnose. Review the generated HTML and content before it goes anywhere near a live site; the destination in this example sits inside the imported project, not a service directory.

If the review turns up problems, keep the Jekyll source untouched and discard or rename only the new target after checking its path carefully. Never use --force as a repair mechanism for a bad import: it just permits writing into a target that already has files, which makes it harder to tell which files came from which attempt.

6. Import into a non-empty target only deliberately

The normal command protects a non-empty target. If you have backed that target up and decided an additive import is actually what you need, pass the documented flag explicitly:

$ cp -a "$HUGO_TARGET" "${HUGO_TARGET}.before-jekyll-import"
$ hugo import jekyll --force "$JEKYLL_ROOT" "$HUGO_TARGET"
Import Jekyll from: /srv/sites/example-jekyll to: /srv/sites/example-hugo-import

Warning

This is the destructive boundary in the whole workflow. The backup command changes state and uses disk space, so check it exists before continuing:

$ test -d "${HUGO_TARGET}.before-jekyll-import" && echo "backup exists"
backup exists

If the forced import turns out to be the wrong call, stop using the target and restore from that backup, verifying both paths first. Restoration can overwrite newer work, so never automate it blindly. The Jekyll source is your other recovery copy.

Common failure checks

  • Two paths missing. Check the command shape and pass both the Jekyll root and the target; a file inside the source is not a substitute for the root directory.
  • Empty or wrong source. Check for _posts or _drafts. The installed command rejects a root that has neither.
  • Target is non-empty. Use a new target for a first attempt, and reserve --force for a reviewed, backed-up merge into an existing directory.
  • Permission denied. Check ownership and permissions with ls -ld. Fixing directory ownership or picking a writable target beats running the whole migration as root.
  • Build fails after import. Keep import success separate from theme and template compatibility: read the build error, then fix the generated project while leaving the source tree alone.

Done means

  • Recorded the version. The installed Hugo version and command path were checked.
  • Confirmed the source. The Jekyll root actually contains the posts or drafts meant for migration.
  • Imported cleanly first. The first attempt used a clean target and completed with exit status 0.
  • Reviewed before trusting it. The target holds imported files, the source is intact, and a test build was checked.
  • Used force with care. --force was only used with an explicit backup, or not used at all.