How can I run a script after a specific package is upgraded?

AFAIK apt does not have package-specific hooks (i.e., there's no way to tell it to run a command when a given package is modified). What it does have is DPkg::Pre-Install-Pkgs:

Pre-Install-Pkgs
   This is a list of shell commands to run before invoking dpkg(1).
   Like options this must be specified in list notation. The commands
   are invoked in order using /bin/sh; should any fail APT will abort.

Note that this is run before anything is done with the packages.

You can specify a version for the information passed to the command via stdin; the level of information increases with the version:

  • Version 1 just sends the paths to the package files being installed
  • Version 2 additionally dumps

    • VERSION 2 as the first line, then
    • the current Apt configuration, followed by a blank line

    and for each package,

    • the versions of the package before and after the operation,
    • as well as the operation itself, so, unlike Version 2,
  • Version 3 in addition to the information in (2) adds architecture information (and has VERSION 3 as the first line)

For a simple case, you could have a script like (in, say /usr/local/bin/restart-foo):

#! /bin/bash

if grep -q my-package
then
    # schedule an at job to restart it
    at now + 30 min <<<"service restart foo"
fi

And apt configuration in /etc/apt/apt.conf.d/99-foo-hook:

DPkg::Pre-Install-Pkgs {"/usr/local/bin/restart-foo";};
DPkg::Tools::Options::/usr/local/bin/restart-foo::Version "1";

For a more complex case, you can request more information and parse it, by setting DPkg::Tools::Options::/usr/local/bin/restart-foo::Version to "2" or "3", like so:

#! /bin/bash
declare -gA version action ver_index
ver_index["VERSION 2"]=3
ver_index["VERSION 3"]=5
skip_options ()
{
    while IFS= read -r line && [[ -n "$line" ]]
    do
        :
    done
}

IFS= read line
case $line in
    "VERSION "[23])
        ver_index="${ver_index[$line]}"
        skip_options
        while read -ra pack
        do
            echo "${pack[@]}"
            version["${pack[0]}"]="${pack[$ver_index]}"
            action["${pack[0]}"]="${pack[-1]}"
        done
        ;;
    *)
        while read pack ver
        do
            version["$pack"]="$ver"
            action["$pack"]="**CONFIGURE**"
        done < <( (echo "$line"; cat ) | xargs -d '\n' dpkg-deb --show --showformat='${Package} ${Version}\n')
        ;;
esac

for i in "${!version[@]}"
do
    echo "$i" "${version[$i]}" "${action[$i]}"
done

Of course, instead of echoing the data, you'd use it to decide what to do.

Examples of input provided:

V1:

/var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb

V2:

VERSION 2
APT::Architecture=amd64
APT::Build-Essential::=build-essential
APT::Install-Recommends=true
[...]
DPkg::Progress-Fancy=1
Binary=apt
CommandLine::AsString=apt%20install%20--reinstall%20tcsh

tcsh - < 6.18.01-5 /var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb
tcsh - < 6.18.01-5 **CONFIGURE**

Version 3:

VERSION 3
APT::Architecture=amd64
APT::Build-Essential::=build-essential
APT::Install-Recommends=true
[...]
DPkg::Progress-Fancy=1
Binary=apt
CommandLine::AsString=apt%20install%20--reinstall%20tcsh
tcsh 6.18.01-5 amd64 none = 6.18.01-5 amd64 none /var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb
tcsh 6.18.01-5 amd64 none = 6.18.01-5 amd64 none **CONFIGURE**

The other way around is to use dpkg triggers. For this, however, you will need to create a package which installs a trigger for something from the package you want to watch, but the triggers are executed after the package is configured, so this is better for restarting services. This Stack Overflow post has a good guide to that.