private boolean removeRetiredNodes(ApplicationId application) {
        try (MaintenanceDeployment deployment = new MaintenanceDeployment(application, deployer, metric, nodeRepository())) {
            if (!deployment.isValid()) {
                log.info("Skipping invalid deployment for " + application);
                return false;
            }
            boolean redeploy = false;
            List<String> nodesToDeactivate = new ArrayList<>();
            try (var lock = nodeRepository().applications().lock(application)) {
                NodeList activeNodes = nodeRepository().nodes().list(Node.State.active);
                Map<Removal, NodeList> nodesByRemovalReason = activeNodes.owner(application)
                                                                         .retired()
                                                                         .groupingBy(node -> removalOf(node, activeNodes));
                for (var kv : nodesByRemovalReason.entrySet()) {
                    Removal reason = kv.getKey();
                    if (reason.equals(Removal.none())) continue;
                    redeploy = true;
                    nodesToDeactivate.addAll(kv.getValue().hostnames());
                    nodeRepository().nodes().setRemovable(kv.getValue(), reason.isReusable());
                }
            }
            if (!redeploy) {
                return true;
            }
            Optional<Long> session = deployment.activate();
            String nodeList = String.join(", ", nodesToDeactivate);
            if (session.isEmpty()) {
                log.info("Failed to redeploy " + application);
                return false;
            }
            log.info("Redeployed " + application + " at session " + session.get() + " to deactivate retired nodes: " + nodeList);
            return true;
        }
    }
Mark retired nodes as removable and redeploy application
node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirer.java