How to mirror and manage repositories with the API



Repository management requires the use of the Landscape API. Set it up and have it ready for the next steps. Linux distributions like Ubuntu use repositories to hold packages you can install on managed computers. While Ubuntu has several repositories that anyone can access, you can also maintain your own repositories on your network, and enforce repository configurations for the machines you manage. This can be useful when you want to maintain packages with different versions from those in the community repositories, or if you have private repositories of in-house software for internal distribution. Once you add your machines to a Landscape repository profile, you have a choice to enforce the entire repository configuration, or just individual repositories.

In the following instructions we will use:

  • distribution: ubuntu
  • series: bionic
  • pockets: release, updates, security
  • components: main, restricted, universe, multiverse
  • tag: example-tag
  • repository profile: example-profile
  • mirror-key: the name of the gpg key used by Landscape to sign your repository

Make sure you have set up the API client already, then follow the steps below. The following instructions were performed on a Ubuntu 18.04 LTS with Landscape server 19.01 installed.

ⓘ Note: The special pocket release never gets mentioned in a suite.

In a sources.list line, you would see:


Disk space requirements

As of March 2024, these are the estimates for the amount of disk space needed to download the following Ubuntu distributions:

Series amd64 i386 Both
Noble 150GB 110GB 180GB
Jammy 330GB 120GB 360GB
Focal 430GB 105GB 445GB
Bionic 290GB 155GB 355GB

Packages will be downloaded to /var/lib/landscape/landscape-repository/standalone/. These estimates are a breakdown of the total size of the pockets for the main, restricted, universe and multiverse components of the amd64 and i386 architectures (release, updates and security pockets). The last column provides an estimate for downloading both the amd64 and i386 architectures. It’s not a total of the amd64 and i386 disk space requirements because it doesn’t duplicate packages that are present in both architectures.

Note that this is only a subset, and it does not include arm and other architectures. Including these will use more disk space.

Create the gpg key

Create a secret GPG key and import it into Landscape. This will be used by self-hosted Landscape to sign your repository. For this guide, we’ll create a key with the ‘real name’ of Mirror Key.

First step is to install and run rngd to speed-up the creation of the gpg key.

sudo apt-get install rng-tools && sudo rngd -r /dev/urandom

Then, create the key that will be used to sign your repository. You can use either of the following commands, but --gen-key creates a GPG key that sets a two year expiration date, and --full-gen-key creates a GPG key that does not expire.

gpg --gen-key
gpg --full-gen-key

You will be prompted twice for:

Please enter the passphrase to protect your new key
Do not set any password. Choose `<OK>` to continue.

Next, at "Please confirm that you do not want to have any protection on your key" choose:

<Yes, protection is not needed>

Note: the secret key must NOT have a passphrase. To remove the passphrase from a key, before exporting it, use:

gpg --edit-key A1234B5678C9101112D12141516E17181920FGH0

See the gpg man page for more details.

List the keys:

gpg -K sec rsa3072 2019-02-05 [SC] [expires: 2021-02-04] A1234B5678C9101112D12141516E17181920FGH0 uid [ultimate] Mirror Key ssb rsa3072 2019-02-05 [E] [expires: 2021-02-04]

Copy the new key’s numeric ID and export the key to a file:

gpg -a --export-secret-keys A1234B5678C9101112D12141516E17181920FGH0 > mirror-key.asc

Import the key file to Landscape:

landscape-api import-gpg-key mirror-key mirror-key.asc --json

    'fingerprint': 'e144:5a89:bc8d:4fbc:49bc:5947:5eb9:93be:549a:8d7a',
    'has_secret': true,
    'id': 1,
    'key_id': '1BE771BB147D6E6D',
    'name': 'mirror-key'

Create the distribution, series and pockets

Create the distribution first:

landscape-api create-distribution ubuntu

Create the series and the pockets which are what hold the actual packages. This will create a bionic series, with pockets for release, security and updates. For components, we select main, restricted, universe and multiverse. It won’t download any packages yet, just create the logical infrastrcuture for them.

landscape-api create-series bionic ubuntu \
 --pockets release,updates,security \
 --components main,restricted,universe,multiverse \
 --architectures amd64,i386 \
 --gpg-key mirror-key \
 --mirror-uri \
 --mirror-series bionic

Sync pockets

If you’re running Landscape on Jammy 22.04 or later, you may need to change the default timeout of 30 minutes in RabbitMQ for your pockets to sync successfully. For more information, see how to configure RabbitMQ for Jammy 22.04 or later.

We can sync only one pocket at a time. Once one pocket sync is done, we can start the next one. This command will start the actual mirroring process for the release pocket:

landscape-api sync-mirror-pocket release bionic ubuntu --json

    'children': [],
    'computer_id': null,
    'creation_time': '2023-10-14T19:45:51Z',
    'creator': {
        'email': '',
        'id': 1,
        'name': 'Stan Peters'
    'id': 101,
    'parent_id': null,

This will create an activity called "Sync pocket 'release' of series 'bionic' in distribution 'ubuntu'" that will be visible at

Depending on your connection speed to the archive, the above may take a few hours. The output of the sync activity you just initiated will show an id. To monitor its progress, you can run a query on the activity id plus one. If the id in the output was 101, we’ll query for 102 and inspect the progress percent result, which in this example is 75.

landscape-api get-activities --json --query id:102 (...)

        'id': 102,
        'parent_id': null,
        'pocket_id': 5,
        'pocket_name': 'release',
        'progress': 75,

It’s almost done. Once it’s finished the 'progress' will be 100 and the activity Status in the WebUI will show Succeeded. Now, we can issue another call, this time to sync the updates pocket. As soon as this one is completed, we can finally sync the security pocket.

landscape-api sync-mirror-pocket updates bionic ubuntu
landscape-api sync-mirror-pocket security bionic ubuntu

The repositories are also visible via a web browser at:


Create a repository profile

We will create a repository profile named example-profile that later will be associated with a tag named example-tag. This profile will be applied to all computers that have that tag.

landscape-api create-repository-profile \
 --description "This profile is for self-hosted Landscape servers." example-profile \

    'all_computers': false,
    'description': 'This profile is for self-hosted Landscape servers.',
    'id': 5,
    'name': 'example-profile',
    'pockets': [],
    'tags': []

Associate computers with repository profile

This will associate all the computers with the tag example-tag to the example-profile repository profile:

landscape-api associate-repository-profile \
 --tags example-tag example-profile \

    'all_computers': false,
    'description': 'This-profile-is-for-self-hosted-Landscape-servers.',
    'id': 5,
    'name': 'example-profile',
    'pockets': [],
    'tags': [

Add pockets to the repository profile

landscape-api add-pockets-to-repository-profile example-profile release,updates,security bionic ubuntu

At the end of this activity, all computers that have the example-tag will get an entry in /etc/apt/sources.list.d/ pointing to the newly created repository for Bionic. This will create the /etc/apt/sources.list.d/landscape-example-profile.list sources file containing the following apt lines:

deb bionic-security main restricted universe multiverse
deb bionic main restricted universe multiverse 
deb bionic-updates main restricted universe multiverse

Landscape will take over the client’s apt sources. The original sources.list file will be moved aside and only the ones enabled in Landscape will work.

cat /etc/apt/sources.list # Landscape manages repositories for this computer # Original content of sources.list can be found in

To revert the changes, disassociate the tag from the repository profile:

landscape-api disassociate-repository-profile --tags example-tag example-profile

Edit pockets

If needed, pockets can be modified using edit-pocket. For example, to change the archive mirror address:

landscape-api edit-pocket release bionic ubuntu \
 --mirror-uri \
 --mirror-suite bionic

This will change the mirror_uri for the release pocket of the bionic series in the ubuntu distribution from:


Using an https source could be useful in situation where a content filtering interferes with the pocket sync when using an http source. Some of the Official Archive Mirrors for Ubuntu are available over https although they are not advertised in the mirror list. For more options have a loot at landscape-api edit-pocket --help.

Upload pockets

Removing a package from a pocket is only supported in upload mode. Landscape lets you create and manage repositories that hold packages uploaded by authorized users. You could, for example, create a staging area to which certain users could upload packages. Here is a quick howto for creating and uploading packages to such a repository.

Create another gpg key with no passphrase and the “real name” of Upload Key. Export the key from gpg and import it to Landscape.

gpg -a --export-secret-keys $KEYID > upload-key.asc
landscape-api import-gpg-key upload-key upload-key.asc
landscape-api get-gpg-keys

If the above was successful, you will have two gpg keys in Landscape.

Assuming this pocket is for bionic and that the ubuntu distribution is created already, this command will create the upload type pocket:

landscape-api create-pocket staging bionic ubuntu main amd64 upload upload-key


  • staging: the name of our upload pocket
  • ubuntu: the distribution (create-distribution ubuntu)
  • bionic: the series (create-series bionic ubuntu)
  • main: the component
  • amd64: the architecture
  • upload: the pocket type
  • upload-key: a private passphrase-less GPG key

Such a repository will be accessible via this sources.list entry:

deb bionic-staging main

You can choose who is allowed to upload packages to this pocket. Since the option --upload-allow-unsigned was not used when creating the pocket, only uploads signed by any of the uploader gpg keys will be allowed. Unsigned uploads, or signed by a key not in that list, will be rejected. To add or remove a key from that list, use add-uploader-gpg-keys-to-pocket and remove-uploader-gpg-keys-from-pocket respectively.

To upload packages to this pocket we use the tool dput with this configuration section in ~/

fqdn =
method = https
incoming = /upload/standalone/%(lds)s

The %(lds)s bit will be replaced by whatever follows the lds: prefix in the dput command shown below. The package debian/changelog file must contain the bionic-staging target, like this example:

my-package (1.0-0ubuntu1) bionic-staging; urgency=low

 * Released 1.0 

-- Package Builder <> Tue, 12 Feb 2023 14:57:05 -0300 (...)

Since Landscape doesn’t build packages from source, you will have to build the package locally for the right architecture and then upload the binary changes file, like this:

dput lds:ubuntu/bionic/staging my-package_1.0-0ubuntu1_amd64.changes

ⓘ Note the path components: lds:distro/series/pocket.

If there are errors, they will be logged in the server’s /var/log/landscape/package-upload-1.log log file. An email will also be sent to the uploader detailing the error.

For example, let’s say you forgot to add the public GPG key of a developer to this pocket. If he tries to upload a package, he will get an error like this back in an email:

Subject: Package import failed for 'storm_0.18.1-0landscape4_amd64.changes' The following error(s) occurred in package import: * Uploaded data is not signed, but the destination pocket requires it * The signature on following file(s) could not be verified. Please make sure to import the GPG key(s) used to sign the files or use a different key to sign them. - storm_0.18.1-0landscape4_amd64.changes (key id 784259B3F3DDC290)

To fix this, we authorize uploads signed by that GPG key. Exported to a file:

gpg -a --export-secret-key 784259B3F3DDC290 > 784259B3F3DDC290.pem

Import it into Landscape:

landscape-api import-gpg-key key-f3ddc290 ./784259B3F3DDC290.pem --json

    'fingerprint': '6f96:333f:ae2c:0ce5:254a:8742:7842:59b3:f3dd:c290',
    'has_secret': true,
    'id': 7,
    'name': 'key-f3ddc290'

Authorize it:

landscape-api add-uploader-gpg-keys-to-pocket staging bionic ubuntu key-f3ddc290 --json (...)

    'upload_allow_unsigned': false,
    'upload_gpg_keys': [
            'fingerprint': '6f96:333f:ae2c:0ce5:254a:8742:7842:59b3:f3dd:c290',
            'has_secret': True,
            'id': 7,
            'name': 'key-f3ddc290'

If you don’t want to use gpg keys, change the pocket to allow unsigned packages:

landscape-api edit-pocket staging bionic ubuntu --upload-allow-unsigned=true

Pull pockets

Pull mode pockets are meant to “stage” packages coming from another pocket, for example if you want to exclude some packages from that pocket, you can use filters (blacklist/whitelist) and then pull packages in the pocket again. Note that since pull operations don’t really fetch packages, but just builds repository indices, you can also remove and recreate pull pockets very quickly if you want to start from scratch with filters. When creating the pull type pocket, you have to specify if you want a whitelist or blacklist for filtering.

landscape-api create-pocket \
 release-staging bionic ubuntu \
 main,universe amd64 mirror mirror-sign-key \
 --pull-pocket release \

The blacklist only works only for pull type repositories, but those don’t support syncing from upstream archives, only from local ones.

Repository management mirroring tips

Here are some simple tips on how to create standard repositories using Landscape. They all use the create-pocket API call, so to use them, you must have created a distribution (for example ubuntu, using a command like create-distribution ubuntu) and a series (for instance bionic, with a command like create-series bionic ubuntu). For complete create-pocket syntax, run the command landscape-api create-pocket -h. Suppose you want to mirror an upstream repository. Basic usage looks like this:

landscape-api create-pocket \
 [--mirror-suite <MIRROR-SUITE>] \
 [--mirror-uri <MIRROR-URI>]

In this command, landscape-api and create-pocket are constants; the rest are variables. The values in [brackets] are optional.

For MIRROR-SUITE, use the release designation; you can add an optional suffix to the name if you want only certain packages, such as all updates or only security updates.

  • pocketname should match the suffix of the mirror-suite, or if you’re mirroring an entire release, use release.
  • series is a release nickname, such as xenial or bionic.
  • The distribution is ubuntu.
  • The component name may be one or more components (main, restricted, universe, multiverse) separated by commas.
  • The architecture may be amd64, i386 or armhf separated by commas.
  • Pocket mode may be pull, mirror, or upload.
  • The gpgkey is the private passphrase-less GPG key that you created above.

Here’s an example with some values filled in:

landscape-api create-pocket \
 release bionic ubuntu \
 main,universe amd64 mirror mirror-sign-key \
 --mirror-suite bionic \

In this command, --mirror-suite indicates you want to create a pocket to mirror a series (bionic) as it was released, without any updates. To create a pocket to mirror updates for a series, add the -updates suffix on the series name:

landscape-api create-pocket \
 updates bionic ubuntu \
 main,universe amd64 mirror mirror-sign-key \
 --mirror-suite bionic-updates \

To create a pocket to mirror only security updates for a series, use the -security suffix:

landscape-api create-pocket \
 security bionic ubuntu \
 main,restricted,universe,multiverse amd64 mirror mirror-key \
 --mirror-suite bionic-security \

The specific suffix is not significant. You could theoretically choose a different convention for pocket names, but we suggest you stick to this usage. Once you’ve created the pocket or pockets you want to use, call sync-mirror-pocket to start the mirroring process:

landscape-api sync-mirror-pocket release bionic ubuntu

Mirror smaller pocket for testing

For testing purposes, instead of mirroring the whole bionic series, you can just mirror the restricted component of the updates pocket:

landscape-api create-series bionic ubuntu \
 --pockets updates \
 --components restricted \
 --architectures amd64 \
 --gpg-key mirror-key \
 --mirror-uri \
 --mirror-series bionic


it is possibile to mirror in a on-premise Landscape 3rd parties repositories?
For instance nvidia cuda packages with its gpg key

Thank you

1 Like

Yes, it is possible to mirror 3rd party repositories. Under the hood, Landscape uses reprepo to mirror repositories.


can you please share how to mirror it? for instance
the i shared before?


1 Like

I did some additional research, Landscape’s repository mirroring capabilities are limited to Canonical’s Ubuntu repositories. Sorry for miscommunicating the information earlier. If you want to mirror additional repositories, you could look into using something like

Please see Mark’s answer below, for the most accurate information.

Give this a try. First, download the gpg key.


Then import the key into Landscape

landscape-api import-gpg-key nvidia-cuda

Next, create the distribution. Note: If you want to mirror other releases 18.04/22.04 make sure to make these separate distributions as well, otherwise the filenames may overlap within the mirror’s directory structure and result in errors about checksums not matching.

landscape-api create-distribution nvidia-cuda-focal

Then create the series, and the release pocket. This is admittedly a bit different than the standard instructions, because nVidia have a flat structure to their repo, and they name things differently.

landscape-api create-series \
--pockets release \
--components main \
--architectures amd64 \
--gpg-key <mirror-key> \
--mirror-gpg-key nvidia-cuda \
--mirror-uri \
--mirror-series / \
focal nvidia-cuda-focal

Sync the pocket to pull in the packages

landscape-api sync-mirror-pocket release focal nvidia-cuda-focal

Monitor the sync process

watch 'landscape-api get-activities --query type:SyncPocketRequest --limit 1'
1 Like

I have an error when I want to removed the distribution “ubuntu”
Error message: Another operation is already in progress on distribution ‘ubuntu’

My /var/lib/landscape/landscape-repository/standalone doesn’t have folder ubuntu anymore. I am stuck there because I cannot remove or add new distribution.


There is a script that you can use to try and get this unstuck. However, make sure that there are no actually active repo activities before running this, as it is not the most sophisticated cleanup.

sudo /opt/canonical/landscape/unblock-repo-activities

Then see if you can perform the other repo tasks again without the message about another operation in progress.



i’m trying to sync nvidia repos listed here:

they are basically

deb$(ARCH) /
deb$(ARCH) /
(yes, they use 18.04 for all major releases)

Following the steps you suggested i created a distro and added a series

landscape-api create-series --pockets release --components main --architectures amd64 --gpg-key mirror-key --mirror-gpg-key nvidia-gpgkey --mirror-uri --mirror-series / jammy libnvidia-container-jammy

but looking at sync status i get:

u’result_text’: u’b"Missing checksums in Release file ‘./lists/update-jammy_%2F_flat_InRelease’!\r\nThere have been errors!\r\n"’,

If i add that repo to a ubuntu jammy machine it works fine, delivering deb packages.

what’s wrong?

Thank you


It looks like we may have hit a bug: when syncing that repo landscape complains about missing checksums but there is one SHA512 as shown:

root@landscape0001:/var/lib/landscape/landscape-repository/standalone/nvidia-container-runtime-jammy/lists# cat update-jammy_%2F_flat_InRelease
Hash: SHA512

Architectures: amd64
Codename: bionic
Components: main
Date: Wed, 27 Sep 2017 23:08:53 +0000
Description: NVIDIA container runtime repository
Suite: bionic
Version: 1.0
a59ede24b2056ad3c111f61aa2b238c66cff99d3a0fea50193964bfdd1489fccb7b51bdd12003b113caf0582b76325cf3dfba3cd476dbda76b6a8f1765176e7c 38026 Packages
708a3c116b6433b86e6ef186c45db40609a149918e0ece4eeac6fffbbe36e8a2525566de57d3f35e7daf24c5e8d51d98eddd2ac460ef5c19a663ff09c8aae61f 7416 Packages.xz


1 Like

Hello, I was able to replicate this in my own setup following the steps as described. I also get the checksum missing error. I suspect that Landscape may be assuming the presence of sha256 checksums and is not currently configured to use the sha512 checksums. I have opened a bug for this on the site. You can follow the progress of the bug there.

1 Like

I think one of the repositories tutorial was removed that used to be in We have packages such as corretto java that needs to be uploaded to a custom pocket. Could you bring that document back please?

Does this page not contain the information you are looking for? Scroll down to the section which covers “Upload Pockets”

I’ve added some custom - non ubuntu mirrors but running into issues with the trying to mirror sync with landscape on microsoft’s ubuntu repos.

landscape-api create-distribution microsoft

landscape-api create-series bionic microsoft
–pockets release
–components main
–architectures amd64
–mirror-gpg-key microsoft-mirror-key
–gpg-key mirror-key
–mirror-series bionic

landscape-api sync-mirror-pocket release bionic microsoft

sync results in the failure

./job-handler.log-20230711:Jul 11 14:09:24 job-handler-1 CRIT Unhandled Error\nTraceback (most recent call last):\nFailure: canonical.reprepro.reprepro.RepreproError: reprepro ended with exit code 255 (out=‘b"Calculating packages to get…\r\nCharacter 0x4d not allowed in sourcename: ‘Microsoft’!\r\nTo ignore use --ignore=forbiddenchar.\r\nThere have been errors!\r\n"’, err=‘b’’’)\n

It seems the uppercase M from Microsoft’s reprepro is causing the issue unless my syntax is wrong.

Just to update this status

With the bug fix this allows to get further but because the flat structure specifying the mirror series (ie: bionic, focal, jammy) won’t work.

results in error when trying to sync pocket release

‘result_code’: 255,
‘result_text’: 'b"aptmethod error receiving ’
"‘’:\\r\\n’404 "
"Not Found [IP: 443]’\r\naptmethod error "
'receiving ’
"‘’:\\r\\n’404 "
"Not Found [IP: 443]’\r\naptmethod error "
‘receiving ’
“‘’:\\r\\n’404 "
"Not Found [IP: 443]’\r\nThere have been "
‘schedule_after_time’: None,
‘schedule_before_time’: None,
‘summary’: "Sync pocket ‘release’ of series ‘jammy’ in distribution "

Is there another way to mirror this flat repo?

The error with the Microsoft repository indicates that an --ignore=forbiddenchar configuration can be used to bypass this issue with the capital letters.

To configure this, create a file called options in the conf directory for the repository in question:

In this file add the ignore option:
ignore forbiddenchar

With this in place, you should be able to run the sync correctly.
landscape-api sync-mirror-pocket release bionic microsoft

I have tested this on Ubuntu Jammy with Landscape 23.03 and this worked for me, so I believe this should work for you as well.

There was a bug related to the mirroring of flat repositories that was fixed in Landscape 23.03+13. This appears to not have been released for Jammy at this time, but has been released for Focal.

landscape-server | 23.03+12.1-0landscape0 | landscape-23.03 | jammy | amd64
landscape-server | 23.03+14-0landscape0   | landscape-23.03 | focal | amd64

If you were willing to try the self-hosted Landscape beta, you could install 23.03+17 on Focal, which should contain the fix from 23.03+13 as well.

landscape-server | 23.03+17-0landscape0 | landscape-beta | focal | amd64
landscape-server | 23.03+17-0landscape0 | landscape-beta | jammy | amd64

I have confirmed that this does resolve the issue with the flat repositories on Jammy, and I was able to mirror the nvidia-cuda repository for both Jammy and Focal successfully on the Landscape 23.03+17 version.

If you would rather not use the beta version, you will need to wait for the stable release for Jammy to be updated sometime in the future, to capture that bug fix.

I’ve managed to setup 23.03 landscape on a new jammy box, I’m on the last part where I need to pull down the repositories for each flavour of Ubuntu that we support, having some issues, are you able to assist please.

When running the below I get back a landscape login page in json format:

landscape-api --key=XXXXXXXXXXXXXXXXXXXXX --secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX sync-mirror-pocket release bionic ubuntu --json --uri=

Hello, I have use landscape standalone (onprem) for years. I have a production version that is operating effectively. However, I wanted to standup a test version, and I was successful in doing everything except where I need to CREATE SERIES using LANDSCAPE-API I am running 23.03 LTS on Jammy OS Whenever I try to execute the CREATE-SERIES for bionic I get failure with Status 500, only issue I saw in the logs was an attempt to open an ldap config file DENIED by apparmor landscape profile. I changed the ENFORCE to COMPLAIN and now I see that it is allowed to open the file, but I am still getting the 500 status code. Is there a known issue with what I described and somewhere that explains these Status codes?

maybe we can assist each other. what are you doing and what error are you getting?