PREREQUISITES

sudo apt-get install apt-mirror update-manager-core

CREATING A LOCAL APT PACKAGE MIRROR

We are going to use apt-mirror to maintain our local APT package mirror. The default configuration file for apt-mirror is /etc/apt/mirror.list.

Below is an example /etc/apt/mirror.list that will create a local package mirror for the most recent version of Ubuntu, 12.10 (Quantal Quetzal):

set base_path         /var/spool/apt-mirror
set var_path          $base_path/var
set nthreads          20
set defaultarch       amd64
set postmirror_script $var_path/postmirror.sh

deb      http://mirrors.us.kernel.org/ubuntu quantal main restricted universe multiverse
deb-src  http://mirrors.us.kernel.org/ubuntu quantal main restricted universe multiverse
deb-i386 http://mirrors.us.kernel.org/ubuntu quantal main restricted universe multiverse

deb      http://mirrors.us.kernel.org/ubuntu quantal-security main restricted universe multiverse
deb-src  http://mirrors.us.kernel.org/ubuntu quantal-security main restricted universe multiverse
deb-i386 http://mirrors.us.kernel.org/ubuntu quantal-security main restricted universe multiverse

deb      http://mirrors.us.kernel.org/ubuntu quantal-updates main restricted universe multiverse
deb-src  http://mirrors.us.kernel.org/ubuntu quantal-updates main restricted universe multiverse
deb-i386 http://mirrors.us.kernel.org/ubuntu quantal-updates main restricted universe multiverse

deb      http://mirrors.us.kernel.org/ubuntu quantal main/debian-installer restricted/debian-installer
deb-i386 http://mirrors.us.kernel.org/ubuntu quantal main/debian-installer restricted/debian-installer

SETTING UP NGINX

For clients to access your local mirror, we are using nginx as a front-end web server. This is the configuration file I use for my mirror. It should be placed in /etc/nginx/sites-available/installer with a symlink to /etc/nginx/sites-enabled/installer.

If you want to use Apache or another web server instead, simply use the web server root in the below example configuration.

server {
  listen 80 default;
  listen [::]:80 default ipv6only=on;

  server_name installer;

  root /var/spool/apt-mirror/mirror/mirrors.us.kernel.org/ubuntu;
  server_name_in_redirect off;

  location / {
    autoindex on;
  }
}

META-RELEASE FILES

As part of the do-release-upgrade process, Ubuntu checks the file /etc/update-manager/meta-release to determine where to look for available versions of Ubuntu.

To trick the update manager into using your local mirror, you need to download the meta-release and meta-release-lts files into your local mirror and modify them slightly, replacing archive.ubuntu.com with the name of your local mirror, apt-mirror.example.com.

#!/bin/bash

MIRROR_DIR="/var/spool/apt-mirror/mirror/mirrors.us.kernel.org/ubuntu"
WGET="wget -qc -O -"

${WGET} http://changelogs.ubuntu.com/meta-release \
  | sed -e 's/archive.ubuntu.com/apt-mirror.example.com/g' > ${MIRROR_DIR}/meta-release
${WGET} http://changelogs.ubuntu.com/meta-release-lts \
  | sed -e 's/archive.ubuntu.com/apt-mirror.example.com/g' > ${MIRROR_DIR}/meta-release-lts

Now, modify /etc/update-manager/meta-release so that Ubuntu will use your local mirror – and modified meta-release(-lts) files – when checking for available versions of Ubuntu.

[METARELEASE]
URI = http://apt-mirror.example.com/ubuntu/meta-release
URI_LTS = http://apt-mirror.example.com/ubuntu/meta-release-lts

If you are currently running an LTS (long-term supported) version of Ubuntu, or upgrading to an LTS version of Ubuntu, you will also need to modify /etc/update-manager/release-upgrades.

DIST-UPGRADER-ALL PACKAGES

The meta-release and meta-release-lts files reference packages that need to be downloaded as part of the upgrade process. apt-mirror does not copy these for you by default, so you need to copy them into your local package mirror manually.

The best place to do that is in /var/spool/apt-mirror/var/postmirror.sh, which is run by apt-mirror before it completes.

The below script is easily extensible for additonal versions of Ubuntu, simply add additional distributions to the dists array.

#!/bin/bash

RSYNC="rsync -rtlH --delete --delete-after"
REMOTE_URL="rsync://mirrors.us.kernel.org/ubuntu"
MIRROR_DIR="/var/spool/apt-mirror/mirror/mirrors.us.kernel.org/ubuntu"

dists=(quantal)

for dist in "${dists[@]}"
do
  ${RSYNC} ${REMOTE_URL}/dists/${dist}-updates/main/dist-upgrader-all/current/${dist}.tar.gz \
    ${MIRROR_DIR}/dists/${dist}-updates/main/dist-upgrader-all/current/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}-updates/main/dist-upgrader-all/current/${dist}.tar.gz.gpg \
    ${MIRROR_DIR}/dists/${dist}-updates/main/dist-upgrader-all/current/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}-updates/main/dist-upgrader-all/current/ReleaseAnnouncement \
    ${MIRROR_DIR}/dists/${dist}-updates/main/dist-upgrader-all/current/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}-updates/main/dist-upgrader-all/current/ReleaseAnnouncement.html \
    ${MIRROR_DIR}/dists/${dist}-updates/main/dist-upgrader-all/current/
done

I18N PACKAGES

Unfortunately, apt-mirror does not copy any of the i18n packages that are required for a successful release upgrade, so you will need to copy them into your local package mirror manually. The best place to do that is in /var/spool/apt-mirror/var/postmirror.sh, which is run by apt-mirror before it completes.

The below script is easily extensible for additonal versions of Ubuntu; simply add additional distributions to the dists array.

#!/bin/bash

RSYNC="rsync -rtlH --delete --delete-after"
REMOTE_URL="rsync://mirrors.us.kernel.org/ubuntu"
MIRROR_DIR="/var/spool/apt-mirror/mirror/mirrors.us.kernel.org/ubuntu"

dists=(quantal quantal-security quantal-updates)

for dist in "${dists[@]}"
do
  ${RSYNC} ${REMOTE_URL}/dists/${dist}/main/i18n/ ${MIRROR_DIR}/dists/${dist}/main/i18n/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}/multiverse/i18n/ ${MIRROR_DIR}/dists/${dist}/multiverse/i18n/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}/restricted/i18n/ ${MIRROR_DIR}/dists/${dist}/restricted/i18n/
  ${RSYNC} ${REMOTE_URL}/dists/${dist}/universe/i18n/ ${MIRROR_DIR}/dists/${dist}/universe/i18n/
done

KEEPING YOUR MIRROR UPDATED

To keep your local mirror updated, run apt-mirror from cron periodically. Create a file /etc/cron.daily/apt-mirror:

#!/bin/bash

/usr/bin/apt-mirror >> /var/spool/apt-mirror/var/apt-mirror.log

DOING THE RELEASE UPGRADE

Finally, set the environment variable RELEASE_UPRADER_ALLOW_THIRD_PARTY, which tells the update manager that it’s okay to use your local mirror.

(Yes, the variable name is spelled incorrectly. It really is RELEASE_UPRADER_ALLOW_THIRD_PARTY, without the “G”.)

If you want to verify, look in /usr/lib/python3/dist-packages/DistUpgrade/DistUpgradeController.py which is part of the python3-distupgrade package.

export RELEASE_UPRADER_ALLOW_THIRD_PARTY=1

Now do the upgrade!

do-release-upgrade