Icon
Sreeram Venkitesh
Published on

How does Kubernetes calculate the age of resources?

642 words • 4 min read

⚠️ Wordsmithing in progress! ⚠️

This post is still a work in progress. I have more information that I'm writing about the TypeMeta and ObjectMeta structs in Kubernetes that I might add to this post later on.

Recently I did an exercise where I learnt how the age of resources is calculated in Kubernetes. This post takes you through the code and helps you understand age is implemented in Kubernetes.

When you list your resources with kubectl get, you get a nice table that formats the age of your resources in a human readable format.

$ k get nodes
NAME                            STATUS   ROLES           AGE   VERSION
my-kind-cluster-control-plane   Ready    control-plane   1d    v1.32.5

Where does this value come from in etcd and how is it processed to finally end up here?

We can figure out easily with kubectl describe or kubectl get -o yaml that the node's spec or status doesn't have an age field. We do have the creationTimestamp in the metadata, which is used to calculate the age.

$ k get node my-kind-cluster-control-plane -o yaml | grep -e metadata -e creationTimestamp
metadata:
  creationTimestamp: "2025-11-21T11:01:36Z"

In the case of the kubectl get output, the raw data is processed into a table before it is printed. The table package in apimachinery is what does this. If you write CRDs, the table response for your CRD is created by the tableconvertor.

Coming back to our node, we can see that the MetaToTableRow() function in the table package is what converts our raw runtimeObj into a metav1.TableRow object that is printed in the kubectl get output.

In this line in MetaToTableRow, we see that the creationTimestamp is passed to a different function named ConvertToHumanReadableDateType().

This function is conveniently placed in the same file below MetaToTableRow, and we can see that this converts our creationTimestamp into the human readable age field, something like 5m or 3y145d.

Now that we have established that the age field in our statuses comes from creationTimestamp, let's see where the creationTimestamp is set when a node is created.

Where does metadata.creationTimestamp come from?

Since every resource in Kubernetes implements the metav1.Object interface, this is where we need to look to find the logic for setting our creationTimestamp. And if you take a quick look at the interface, you can see the SetCreationTimestamp() method in the interface. All Kubernetes objects (which implements metav1.Object) would have access to a SetCreationTimestamp method. The implementation of this method comes from ObjectMeta and is shared across all the resources in Kubernetes by embedding ObjectMeta into each resource type. In short, all the resources have access to a shared implementation.

The implementation of ObjectMeta's SetCreationTimestamp is done here:

func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp Time) {
	meta.CreationTimestamp = creationTimestamp
}

And since our Node type embeds ObjectMeta, our node objects also have access to this implementation:

type Node struct {
	metav1.TypeMeta `json:",inline"`

	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

	Spec NodeSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

	Status NodeStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

So where is this being used? If you look at the code for how the apiserver persists data into etcd, we can see that a function rest.FillObjectMetaSystemFields() is actually responsible for using the SetCreationTimestamp() function to set the current time into the creationTimestamp field of the metav1.Object that is passed into the function.

From the staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go:

// metav1Now returns metav1.Now(), but allows override for unit testing
var metav1Now = metav1.Now

// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta.
func FillObjectMetaSystemFields(meta metav1.Object) {
	meta.SetCreationTimestamp(metav1Now())
	meta.SetUID(uuid.NewUUID())
}

So when we see a node's age, it is actually the time from when the node object was persisted into etcd and not the age of the kubelet process or the uptime of the node. Learning how age works in Kubernetes helped me solidify my mental model of how Kubernetes stores and processes information in etcd.