Rebuilding a board's fields and views by hand for a new team wastes an afternoon, and gh project copy clones the whole layout in one command. The examples use GitHub CLI 2.87.3, installed here on 23 September 2026.
Allow about ten minutes, plus the time needed to authenticate to GitHub and inspect the project. You need the gh command, permission to read the source project and permission to create a project for the target owner. This guide covers the installed gh project copy command, and it does not create a project until step 4.
Confirm the version and read the command's local help. These are ordinary, read-only commands and do not need elevated privileges:
$ gh version
gh version 2.87.3 (2026-02-23)
$ gh project copy --help
The command accepts an optional project number followed by flags. The installed interface includes --source-owner, --target-owner and --title, plus --drafts and the usual JSON, jq and Go template output options.
Checkpoint: if gh project copy --help does not show these options, stop and check the installed version before using the examples. Do not mix syntax from a different GitHub CLI release.
Project numbers are not repository numbers. Start with the number shown by the GitHub project you intend to copy. Write down the source owner, target owner and the title you want the new project to have:
$ PROJECT_NUMBER=1
$ SOURCE_OWNER='monalisa'
$ TARGET_OWNER='github'
$ NEW_TITLE='A new project'
The owner values are GitHub logins. The command accepts @me for the current user in either owner option, which is useful when the destination is your own account:
$ SOURCE_OWNER='monalisa'
$ TARGET_OWNER='@me'
Keep the values as separate shell variables rather than assembling an unquoted command string. Quoting protects spaces and shell metacharacters in a title. It does not grant permission to the owner, and it does not turn a repository number into a project number.
By default, the command does not include draft issues. Add --drafts only when the destination should contain them as well:
$ INCLUDE_DRAFTS=1
$ if [ "$INCLUDE_DRAFTS" = 1 ]; then
> DRAFTS_FLAG=(--drafts)
> else
> DRAFTS_FLAG=()
> fi
This is an ordinary shell decision and changes nothing on GitHub. The trap here is assuming a copied project always contains every item from the source: draft inclusion is an explicit choice in this command, so settle it before you run a mutating command.
Checkpoint: read the final values back before proceeding:
$ printf 'project: %s\nsource: %s\ntarget: %s\ntitle: %s\n' \
"$PROJECT_NUMBER" "$SOURCE_OWNER" "$TARGET_OWNER" "$NEW_TITLE"
project: 1
source: monalisa
target: github
title: A new project
Warning: copying creates a new GitHub project, so treat this as a state-changing operation. There is no dry-run flag in the installed command. Before pressing Enter, verify that the number, both owners and the title in this command are correct:
$ gh project copy "$PROJECT_NUMBER" \
--source-owner "$SOURCE_OWNER" \
--target-owner "$TARGET_OWNER" \
--title "$NEW_TITLE" \
"\${DRAFTS_FLAG[@]}"
The command's manpage documents this shape and gives the equivalent concrete example gh project copy 1 --source-owner monalisa --target-owner github --title "a new project". The exact success output is not specified by the local manpage and can vary with the CLI release. Read it rather than scripting against an assumed sentence or URL.
Do not use sudo. Elevated local privileges do not change GitHub permissions and are not required for this command. If authentication or authorisation fails, fix the GitHub account, organisation access or token scopes instead of retrying with root privileges.
If you need machine-readable output, request JSON explicitly. The installed command documents --format json; jq and Go templates are filters for that formatted output:
$ gh project copy "$PROJECT_NUMBER" \
--source-owner "$SOURCE_OWNER" \
--target-owner "$TARGET_OWNER" \
--title "$NEW_TITLE" \
"\${DRAFTS_FLAG[@]}" \
--format json
Save the returned project number or URL in your change record. Then open the new project in GitHub and check its owner, title, fields, views and item set. A successful command means the copy request completed; it is still worth checking the result before you share or automate the new project.
To select fields from JSON, use a jq expression only after seeing the actual response shape:
$ gh project copy "$PROJECT_NUMBER" \
--source-owner "$SOURCE_OWNER" \
--target-owner "$TARGET_OWNER" \
--title "$NEW_TITLE" \
--format json \
--jq '.'
The expression . asks for the complete JSON value. Do not guess property names from a different command. If the project was created but your terminal lost the response, search the target owner's projects in GitHub before trying the copy again, because a retry may create a second project.
A non-zero exit status means the command did not report a successful completion. Check the project number, owner spelling, authentication and permissions first. A missing source project and a destination policy restriction are different problems, so preserve the error text when asking an administrator for help.
Recovery: if the command appears to fail after GitHub may have accepted it, do not immediately repeat the copy. Check the target owner's project list and search by the title you supplied. If an unwanted project exists, remove that newly created project using the target owner's normal GitHub project controls after confirming its number. This is irreversible for that project, so record the number and contents before deleting it.
The copy operation does not modify the source project. There is no undo flag documented for gh project copy, and changing the source after the copy will not be a rollback. Treat the new project as a separate object and verify it before making further edits.