Repository: kurtosis-tech/kurtosis
Pull request #1939 (2023-12-13 06:18:35)

feat: allow specifying size of persistent directories
## Description:
Introduces the ability to have persistent directories of a given size using `size`

## Is this change user facing?
YES

cloudflare-pages[bot]: ## Deploying with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>ea0f496</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://9df303fb.kurtosis-docs.pages.dev'>https://9df303fb.kurtosis-docs.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://gyani-size.kurtosis-docs.pages.dev'>https://gyani-size.kurtosis-docs.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/?to=/:account/pages/view/kurtosis/9df303fb-b7e7-451c-bc43-73361342684e)


Patch:
diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go
index a0dbbfbb6..cf52d7a4e 100644
--- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go
+++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go
@@ -14,17 +14,17 @@ func getOrCreatePersistentDirectories(
 	ctx context.Context,
 	serviceUuid service.ServiceUUID,
 	objAttrsProvider object_attributes_provider.DockerEnclaveObjectAttributesProvider,
-	serviceMountpointsToPersistentKey map[string]service_directory.DirectoryPersistentKey,
+	serviceMountpointsToPersistentKey map[string]service_directory.PersistentDirectory,
 	dockerManager *docker_manager.DockerManager,
 ) (map[string]string, error) {
 	shouldDeleteVolumes := true
 	volumeNamesToRemoveIfFailure := map[string]bool{}
 	persistentDirectories := map[string]string{}
 
-	for serviceDirPath, persistentKey := range serviceMountpointsToPersistentKey {
-		volumeAttrs, err := objAttrsProvider.ForSinglePersistentDirectoryVolume(serviceUuid, persistentKey)
+	for serviceDirPath, persistentDirectory := range serviceMountpointsToPersistentKey {
+		volumeAttrs, err := objAttrsProvider.ForSinglePersistentDirectoryVolume(serviceUuid, persistentDirectory.PersistentKey)
 		if err != nil {
-			return nil, stacktrace.Propagate(err, "Error creating persistent directory labels for '%s'", persistentKey)
+			return nil, stacktrace.Propagate(err, "Error creating persistent directory labels for '%s'", persistentDirectory.PersistentKey)
 		}
 
 		volumeName := volumeAttrs.GetName().GetString()
@@ -45,11 +45,15 @@ func getOrCreatePersistentDirectories(
 			return nil, stacktrace.NewError("More than one volume with name '%s' exists in docker. This is unexpected", volumeName)
 		}
 
+		// note this doesn't consider persistentDirectory.Size
+		// Docker doesn't support sized volumes - the best you can do is create tmpfs (in memory) persistent volumes
+		// but that would go away if you restart Docker
+		// TODO Make the `lsp` aware of `size`
 		if err = dockerManager.CreateVolume(ctx, volumeName, volumeLabelsStrs); err != nil {
 			return nil, stacktrace.Propagate(
 				err,
 				"An error occurred creating persistent directory volume '%s' for service '%v'",
-				persistentKey,
+				persistentDirectory.PersistentKey,
 				serviceUuid,
 			)
 		}
diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/start_user_services.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/start_user_services.go
index b8f542e46..d04897e59 100644
--- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/start_user_services.go
+++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/start_user_services.go
@@ -593,7 +593,7 @@ func createStartServiceOperation(
 				ctx,
 				serviceUUID,
 				enclaveObjAttrsProvider,
-				persistentDirectories.ServiceDirpathToDirectoryPersistentKey,
+				persistentDirectories.ServiceDirpathToPersistentDirectory,
 				dockerManager,
 			)
 			if err != nil {
diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go
index cf668f2ab..b64e4e8e6 100644
--- a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go
+++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go
@@ -414,19 +414,7 @@ func (manager *DockerManager) CreateVolume(context context.Context, volumeName s
 		Name:              volumeName,
 	}
 
-	/*
-		We don't use the return value of VolumeCreate because there's not much useful information on there - Docker doesn't
-		use UUIDs to identify volumes - only the name - so there's no UUID to retrieve, and the volume's Mountpoint (what you'd
-		think would be the path of the volume on the local machine) isn't useful either because Docker itself runs inside a VM
-		so *this path is only a path inside the Docker VM* (meaning we can't use it to read/write files). AFAICT, the only way
-		to read/write data to a volume is to mount it in a container. ~ ktoday, 2020-07-01
-	*/
-	_, err := manager.dockerClient.VolumeCreate(context, volumeConfig)
-	if err != nil {
-		return stacktrace.Propagate(err, "Could not create Docker volume for test controller")
-	}
-
-	return nil
+	return manager.createPersistentVolumeInternal(context, volumeConfig)
 }
 
 /*
@@ -1374,6 +1362,23 @@ func (manager *DockerManager) GetAvailableCPUAndMemory(ctx context.Context) (com
 //	INSTANCE HELPER FUNCTIONS
 //
 // =================================================================================================================
+func (manager *DockerManager) createPersistentVolumeInternal(context context.Context, volumeConfig volume.CreateOptions) error {
+	/*
+		We don't use the return value of VolumeCreate because there's not much useful information on there - Docker doesn't
+		use UUIDs to identify volumes - only the name - so there's no UUID to retrieve, and the volume's Mountpoint (what you'd
+		think would be the path of the volume on the local machine) isn't useful either because Docker itself runs inside a VM
+		so *this path is only a path inside the Docker VM* (meaning we can't use it to read/write files). AFAICT, the only way
+		to read/write data to a volume is to mount it in a container. ~ ktoday, 2020-07-01
+	*/
+	_, err := manager.dockerClient.VolumeCreate(context, volumeConfig)
+	if err != nil {
+		return stacktrace.Propagate(err, "Could not create Docker volume for test controller")
+	}
+
+	return nil
+
+}
+
 func (manager *DockerManager) isImageAvailableLocally(imageName string) (bool, error) {
 	// Own context for checking if the image is locally available because we do not want to cancel this works in case the main context in the request is cancelled
 	// if the first request fails the image will be ready for following request making the process faster
diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go
index 0259ff620..37626e9db 100644
--- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go
+++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go
@@ -73,7 +73,7 @@ func preparePersistentDirectoriesResources(
 	namespace string,
 	serviceUuid service.ServiceUUID,
 	objAttributeProviders object_attributes_provider.KubernetesEnclaveObjectAttributesProvider,
-	serviceMountpointsToPersistentKey map[string]service_directory.DirectoryPersistentKey,
+	serviceMountpointsToPersistentKey map[string]service_directory.PersistentDirectory,
 	kubernetesManager *kubernetes_manager.KubernetesManager,
 ) (map[string]*kubernetesVolumeWithClaim, error) {
 	shouldDeleteVolumesAndClaimsCreated := true
@@ -82,10 +82,10 @@ func preparePersistentDirectoriesResources(
 
 	persistentVolumesAndClaims := map[string]*kubernetesVolumeWithClaim{}
 
-	for dirPath, persistentKey := range serviceMountpointsToPersistentKey {
-		volumeAttrs, err := objAttributeProviders.ForSinglePersistentDirectoryVolume(serviceUuid, persistentKey)
+	for dirPath, persistentDirectory := range serviceMountpointsToPersistentKey {
+		volumeAttrs, err := objAttributeProviders.ForSinglePersistentDirectoryVolume(serviceUuid, persistentDirectory.PersistentKey)
 		if err != nil {
-			return nil, stacktrace.Propagate(err, "An error occurred creating the labels for persist service directory '%s'", persistentKey)
+			return nil, stacktrace.Propagate(err, "An error occurred creating the labels for persist service directory '%s'", persistentDirectory.PersistentKey)
 		}
 
 		volumeName := volumeAttrs.GetName().GetString()
@@ -94,11 +94,13 @@ func preparePersistentDirectoriesResources(
 			volumeLabelsStrs[key.GetString()] = value.GetString()
 		}
 
+		persistentVolumeSize := int64(persistentDirectory.Size)
+
 		var persistentVolume *apiv1.PersistentVolume
 		if persistentVolume, err = kubernetesManager.GetPersistentVolume(ctx, volumeName); err != nil {
-			persistentVolume, err = kubernetesManager.CreatePersistentVolume(ctx, namespace, volumeName, volumeLabelsStrs)
+			persistentVolume, err = kubernetesManager.CreatePersistentVolume(ctx, namespace, volumeName, volumeLabelsStrs, persistentVolumeSize)
 			if err != nil {
-				return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume for '%s'", persistentKey)
+				return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume for '%s'", persistentDirectory.PersistentKey)
 			}
 			volumesCreated[persistentVolume.Name] = persistentVolume
 		}
@@ -106,9 +108,9 @@ func preparePersistentDirectoriesResources(
 		// For now, we have a 1:1 mapping between volume and volume claims, so it's fine giving it the same name
 		var persistentVolumeClaim *apiv1.PersistentVolumeClaim
 		if persistentVolumeClaim, err = kubernetesManager.GetPersistentVolumeClaim(ctx, namespace, volumeName); err != nil {
-			persistentVolumeClaim, err = kubernetesManager.CreatePersistentVolumeClaim(ctx, namespace, volumeName, volumeLabelsStrs)
+			persistentVolumeClaim, err = kubernetesManager.CreatePersistentVolumeClaim(ctx, namespace, volumeName, volumeLabelsStrs, persistentVolumeSize)
 			if err != nil {
-				return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume claim for '%s'", persistentKey)
+				return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume claim for '%s'", persistentDirectory.PersistentKey)
 			}
 			volumeClaimsCreated[persistentVolumeClaim.Name] = persistentVolumeClaim
 		}
diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go
index 0328da729..f48273e48 100644
--- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go
+++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go
@@ -346,7 +346,7 @@ func createStartServiceOperation(
 				namespaceName,
 				serviceUuid,
 				enclaveObjAttributesProvider,
-				persistentDirectories.ServiceDirpathToDirectoryPersistentKey,
+				persistentDirectories.ServiceDirpathToPersistentDirectory,
 				kubernetesManager)
 			if err != nil {
 				return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volumes and claims requested for service '%s'", serviceName)
diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go
index 8a64347b0..6be2a3a99 100644
--- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go
+++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go
@@ -75,10 +75,9 @@ const (
 	volumeHostPathRootDirectory = "/kurtosis-persistent-service-data"
 	// TODO: Maybe pipe this to Starlark to let users choose the size of their persistent directories
 	//  The difficulty is that Docker doesn't have such a feature, so we would need somehow to hack it
-	persistentVolumeDefaultSize                          int64 = 500 * 1024 * 1024 // 500Mb
-	waitForPersistentVolumeBoundTimeout                        = 30 * time.Second
-	waitForPersistentVolumeBoundInitialDelayMilliSeconds       = 100
-	waitForPersistentVolumeBoundRetriesDelayMilliSeconds       = 500
+	waitForPersistentVolumeBoundTimeout                  = 30 * time.Second
+	waitForPersistentVolumeBoundInitialDelayMilliSeconds = 100
+	waitForPersistentVolumeBoundRetriesDelayMilliSeconds = 500
 )
 
 // We'll try to use the nicer-to-use shells first before we drop down to the lower shells
@@ -306,7 +305,12 @@ func (manager *KubernetesManager) CreatePersistentVolume(
 	namespace string,
 	volumeName string,
 	labels map[string]string,
+	requiredSize int64,
 ) (*apiv1.PersistentVolume, error) {
+	if requiredSize == 0 {
+		return nil, stacktrace.NewError("Cannot create volume '%v' of size 0; need size greater than 0", volumeName)
+	}
+
 	volumesClient := manager.kubernetesClientSet.CoreV1().PersistentVolumes()
 
 	// Check that there's only one node, otherwise using HostPath volumes will not work.
@@ -356,7 +360,7 @@ func (manager *KubernetesManager) CreatePersistentVolume(
 		},
 		Spec: apiv1.PersistentVolumeSpec{
 			Capacity: apiv1.ResourceList{
-				apiv1.ResourceStorage: *resource.NewQuantity(persistentVolumeDefaultSize, resource.BinarySI),
+				apiv1.ResourceStorage: *resource.NewQuantity(requiredSize, resource.BinarySI),
 			},
 			PersistentVolumeSource: apiv1.PersistentVolumeSource{
 				GCEPersistentDisk:    nil,
@@ -463,7 +467,12 @@ func (manager *KubernetesManager) CreatePersistentVolumeClaim(
 	namespace string,
 	volumeClaimName string,
 	labels map[string]string,
+	requiredSize int64,
 ) (*apiv1.PersistentVolumeClaim, error) {
+	if requiredSize == 0 {
+		return nil, stacktrace.NewError("Cannot create volume '%v' of 0 size; need a value greater than 0", volumeClaimName)
+	}
+
 	volumeClaimsClient := manager.kubernetesClientSet.CoreV1().PersistentVolumeClaims(namespace)
 
 	volumeClaimsDefinition := apiv1.PersistentVolumeClaim{
@@ -500,7 +509,7 @@ func (manager *KubernetesManager) CreatePersistentVolumeClaim(
 				Requests: apiv1.ResourceList{
 					// we give each claim 100% of the corresponding volume. Since we have a 1:1 mapping between volumes
 					// and claims right now, it's the best we can do
-					apiv1.ResourceStorage: *resource.NewQuantity(persistentVolumeDefaultSize, resource.BinarySI),
+					apiv1.ResourceStorage: *resource.NewQuantity(requiredSize, resource.BinarySI),
 				},
 				Claims: nil,
 			},
diff --git a/container-engine-lib/lib/backend_interface/objects/service_directory/persistent_directories.go b/container-engine-lib/lib/backend_interface/objects/service_directory/persistent_directories.go
index 17250b31d..fc96d20a4 100644
--- a/container-engine-lib/lib/backend_interface/objects/service_directory/persistent_directories.go
+++ b/container-engine-lib/lib/backend_interface/objects/service_directory/persistent_directories.go
@@ -1,13 +1,19 @@
 package service_directory
 
 type DirectoryPersistentKey string
+type DirectoryPersistentSize int64
+
+type PersistentDirectory struct {
+	PersistentKey DirectoryPersistentKey
+	Size          DirectoryPersistentSize
+}
 
 type PersistentDirectories struct {
-	ServiceDirpathToDirectoryPersistentKey map[string]DirectoryPersistentKey
+	ServiceDirpathToPersistentDirectory map[string]PersistentDirectory
 }
 
-func NewPersistentDirectories(serviceDirpathToDirectoryPersistentKey map[string]DirectoryPersistentKey) *PersistentDirectories {
+func NewPersistentDirectories(persistentDirectories map[string]PersistentDirectory) *PersistentDirectories {
 	return &PersistentDirectories{
-		ServiceDirpathToDirectoryPersistentKey: serviceDirpathToDirectoryPersistentKey,
+		ServiceDirpathToPersistentDirectory: persistentDirectories,
 	}
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument/validators.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument/validators.go
index 96d8cb166..605afed4e 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument/validators.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument/validators.go
@@ -36,6 +36,21 @@ func Uint64InRange(value starlark.Value, argNameForLogging string, min uint64, m
 	return nil
 }
 
+func Int64InRange(value starlark.Value, argNameForLogging string, min int64, max int64) *startosis_errors.InterpretationError {
+	valueInt, ok := value.(starlark.Int)
+	if !ok {
+		return startosis_errors.NewInterpretationError("Value for '%s' was expected to be an integer between %d and %d, but it was '%s'", argNameForLogging, min, max, reflect.TypeOf(value))
+	}
+	valueInt64, ok := valueInt.Int64()
+	if !ok {
+		return startosis_errors.NewInterpretationError("Value for '%s' was expected to be an integer between %d and %d, but it was %v", argNameForLogging, min, max, valueInt)
+	}
+	if valueInt64 < min || valueInt64 > max {
+		return startosis_errors.NewInterpretationError("Value for '%s' was expected to be an integer between %d and %d, but it was %v", argNameForLogging, min, max, valueInt64)
+	}
+	return nil
+}
+
 func FloatInRange(value starlark.Value, argNameForLogging string, min float64, max float64) *startosis_errors.InterpretationError {
 	valueStarlarkFloat, ok := value.(starlark.Float)
 	if !ok {
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/directory/directory.go b/core/server/api_container/server/startosis_engine/kurtosis_types/directory/directory.go
index 7591b5963..354b39ae0 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_types/directory/directory.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_types/directory/directory.go
@@ -4,8 +4,10 @@ import (
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_type_constructor"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_constants"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors"
 	"go.starlark.net/starlark"
+	"math"
 )
 
 const (
@@ -13,6 +15,9 @@ const (
 
 	ArtifactNameAttr  = "artifact_name"
 	PersistentKeyAttr = "persistent_key"
+	SizeKeyAttr       = "size"
+
+	atleastOneMegabyte = 1
 )
 
 func NewDirectoryType() *kurtosis_type_constructor.KurtosisTypeConstructor {
@@ -37,6 +42,14 @@ func NewDirectoryType() *kurtosis_type_constructor.KurtosisTypeConstructor {
 						return builtin_argument.NonEmptyString(value, ArtifactNameAttr)
 					},
 				},
+				{
+					Name:              SizeKeyAttr,
+					IsOptional:        true,
+					ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.Int],
+					Validator: func(value starlark.Value) *startosis_errors.InterpretationError {
+						return builtin_argument.Int64InRange(value, SizeKeyAttr, atleastOneMegabyte, math.MaxInt64)
+					},
+				},
 			},
 		},
 
@@ -66,6 +79,7 @@ func CreateDirectoryFromFilesArtifact(
 	args := []starlark.Value{
 		starlark.String(filesArtifactName),
 		nil,
+		nil,
 	}
 
 	argumentDefinitions := NewDirectoryType().KurtosisBaseBuiltin.Arguments
@@ -112,3 +126,19 @@ func (directory *Directory) GetPersistentKeyIfSet() (string, bool, *startosis_er
 	}
 	return persistentKey.GoString(), true, nil
 }
+
+func (directory *Directory) GetSizeOrDefault() (int64, *startosis_errors.InterpretationError) {
+	size, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.Int](
+		directory.KurtosisValueTypeDefault, SizeKeyAttr)
+	if interpretationErr != nil {
+		return 0, interpretationErr
+	}
+	if !found {
+		return startosis_constants.DefaultPersistentDirectorySize, nil
+	}
+	sizeInt64, ok := size.Int64()
+	if !ok {
+		return 0, startosis_errors.NewInterpretationError("Couldn't convert size '%v' to int64", size)
+	}
+	return sizeInt64, nil
+}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go
index 2729612a7..2aea12c14 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go
@@ -258,11 +258,7 @@ func ConvertFilesArtifactsMounts(filesArtifactsMountDirpathsMap map[string]strin
 	}, nil
 }
 
-func convertPersistentDirectoryMounts(persistentDirectoriesDirpathsMap map[string]string) *service_directory.PersistentDirectories {
-	persistentDirectoriesMap := map[string]service_directory.DirectoryPersistentKey{}
-	for dirPath, persistentKeyStr := range persistentDirectoriesDirpathsMap {
-		persistentDirectoriesMap[dirPath] = service_directory.DirectoryPersistentKey(persistentKeyStr)
-	}
+func convertPersistentDirectoryMounts(persistentDirectoriesMap map[string]service_directory.PersistentDirectory) *service_directory.PersistentDirectories {
 	return service_directory.NewPersistentDirectories(persistentDirectoriesMap)
 }
 
@@ -316,8 +312,7 @@ func (config *ServiceConfig) ToKurtosisType(serviceNetwork service_network.Servi
 	}
 	if found {
 		var filesArtifactsMountDirpathsMap map[string]string
-		var persistentDirectoriesDirpathsMap map[string]string
-		filesArtifactsMountDirpathsMap, persistentDirectoriesDirpathsMap, interpretationErr = convertFilesArguments(FilesAttr, filesStarlark)
+		filesArtifactsMountDirpathsMap, persistentDirectoriesDirpathsMap, interpretationErr := convertFilesArguments(FilesAttr, filesStarlark)
 		if interpretationErr != nil {
 			return nil, interpretationErr
 		}
@@ -513,9 +508,9 @@ func convertPortMapEntry(attrNameForLogging string, key starlark.Value, value st
 	return keyStr.GoString(), servicePortSpec, nil
 }
 
-func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict) (map[string]string, map[string]string, *startosis_errors.InterpretationError) {
+func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict) (map[string]string, map[string]service_directory.PersistentDirectory, *startosis_errors.InterpretationError) {
 	filesArtifacts := map[string]string{}
-	persistentDirectories := map[string]string{}
+	persistentDirectories := map[string]service_directory.PersistentDirectory{}
 	for _, fileItem := range filesDict.Items() {
 		rawDirPath := fileItem[0]
 		dirPath, ok := rawDirPath.(starlark.String)
@@ -545,6 +540,10 @@ func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict)
 		if interpretationErr != nil {
 			return nil, nil, interpretationErr
 		}
+		persistentDirectorySize, interpretationErr := directoryObj.GetSizeOrDefault()
+		if interpretationErr != nil {
+			return nil, nil, interpretationErr
+		}
 		if artifactNameSet == persistentKeySet {
 			// this condition is a XOR
 			return nil, nil, startosis_errors.NewInterpretationError("Parameter '%s' and '%s' cannot be set on the same '%s' object: '%s'",
@@ -554,7 +553,10 @@ func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict)
 			filesArtifacts[dirPath.GoString()] = artifactName
 		} else {
 			// persistentKey is necessarily set since we checked the exclusivity above
-			persistentDirectories[dirPath.GoString()] = persistentKey
+			persistentDirectories[dirPath.GoString()] = service_directory.PersistentDirectory{
+				PersistentKey: service_directory.DirectoryPersistentKey(persistentKey),
+				Size:          service_directory.DirectoryPersistentSize(persistentDirectorySize),
+			}
 		}
 	}
 	return filesArtifacts, persistentDirectories, nil
diff --git a/core/server/api_container/server/startosis_engine/startosis_constants/startosis_constants.go b/core/server/api_container/server/startosis_engine/startosis_constants/startosis_constants.go
index 5262687cc..05a8f8c02 100644
--- a/core/server/api_container/server/startosis_engine/startosis_constants/startosis_constants.go
+++ b/core/server/api_container/server/startosis_engine/startosis_constants/startosis_constants.go
@@ -12,4 +12,7 @@ const (
 	PackageIdPlaceholderForStandaloneScript                          = "DEFAULT_PACKAGE_ID_FOR_SCRIPT"
 	PlaceHolderMainFileForPlaceStandAloneScript                      = ""
 	ParallelismParam                            StarlarkContextParam = "PARALLELISM"
+
+	// DefaultPersistentDirectorySize 500 Megabytes is the default value
+	DefaultPersistentDirectorySize int64 = 500 * 1024 * 1024
 )
diff --git a/docs/docs/api-reference/starlark-reference/directory.md b/docs/docs/api-reference/starlark-reference/directory.md
index f4a57f5b4..e631676bd 100644
--- a/docs/docs/api-reference/starlark-reference/directory.md
+++ b/docs/docs/api-reference/starlark-reference/directory.md
@@ -28,6 +28,16 @@ by its `persistent_key` and the service ID on which it is being used (a persiste
 multiple services). When it is first created, it will be empty. The service can write anything in it. When the service 
 gets updated, the data in it persists. It is particularly useful for a service's data directory, logs directory, etc.
 
+A persistent directory of a specific size can be created using the `size` field; the value supplied is in megabytes
+
+```python
+persistent_directory = Directory(
+    size = 1000
+)
+```
+
+The default size of a persistent directory is `500Mb`. Note the size attribute is ignored on Docker due to Docker limitations.
+
 <!--------------- ONLY LINKS BELOW THIS POINT ---------------------->
 [render-templates-reference]: ./plan.md#render_templates
 [service-config]: ./service-config.md


Test patch:
diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go
index 17091924a..6b382f2d0 100644
--- a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go
+++ b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go
@@ -79,9 +79,9 @@ func getServiceConfigForTest(t *testing.T, imageName string) *ServiceConfig {
 }
 
 func testPersistentDirectory() *service_directory.PersistentDirectories {
-	persistentDirectoriesMap := map[string]service_directory.DirectoryPersistentKey{
-		"dirpath1": service_directory.DirectoryPersistentKey("dirpath1_persistent_directory_key"),
-		"dirpath2": service_directory.DirectoryPersistentKey("dirpath2_persistent_directory_key"),
+	persistentDirectoriesMap := map[string]service_directory.PersistentDirectory{
+		"dirpath1": {PersistentKey: service_directory.DirectoryPersistentKey("dirpath1_persistent_directory_key"), Size: service_directory.DirectoryPersistentSize(int64(0))},
+		"dirpath2": {PersistentKey: service_directory.DirectoryPersistentKey("dirpath2_persistent_directory_key"), Size: service_directory.DirectoryPersistentSize(int64(0))},
 	}
 
 	return service_directory.NewPersistentDirectories(persistentDirectoriesMap)
diff --git a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go
index 1fc8207fd..d40a03f28 100644
--- a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go
+++ b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go
@@ -326,9 +326,9 @@ func getServiceConfigForTest(t *testing.T, imageName string) *service.ServiceCon
 }
 
 func testPersistentDirectory() *service_directory.PersistentDirectories {
-	persistentDirectoriesMap := map[string]service_directory.DirectoryPersistentKey{
-		"dirpath1": service_directory.DirectoryPersistentKey("dirpath1_persistent_directory_key"),
-		"dirpath2": service_directory.DirectoryPersistentKey("dirpath2_persistent_directory_key"),
+	persistentDirectoriesMap := map[string]service_directory.PersistentDirectory{
+		"dirpath1": {PersistentKey: service_directory.DirectoryPersistentKey("dirpath1_persistent_directory_key"), Size: service_directory.DirectoryPersistentSize(int64(0))},
+		"dirpath2": {PersistentKey: service_directory.DirectoryPersistentKey("dirpath2_persistent_directory_key"), Size: service_directory.DirectoryPersistentSize(int64(0))},
 	}
 
 	return service_directory.NewPersistentDirectories(persistentDirectoriesMap)
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_file_artifact_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_file_artifact_framework_test.go
index 2b54e31fe..927235cc5 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_file_artifact_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_file_artifact_framework_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/directory"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_constants"
 	"github.com/stretchr/testify/require"
 	"testing"
 )
@@ -35,4 +36,8 @@ func (t *directoryFileArtifactTestCase) Assert(typeValue builtin_argument.Kurtos
 	require.Nil(t, err)
 	require.False(t, found)
 	require.Empty(t, persistentKey)
+
+	size, err := directoryStarlark.GetSizeOrDefault()
+	require.Nil(t, err)
+	require.Equal(t, startosis_constants.DefaultPersistentDirectorySize, size)
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_persistent_directory_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_persistent_directory_framework_test.go
index d6ac99ee8..64e81c595 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_persistent_directory_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/directory_persistent_directory_framework_test.go
@@ -19,7 +19,7 @@ func (suite *KurtosisTypeConstructorTestSuite) TestDirectoryPersistentDirectory(
 }
 
 func (t *directoryPersistentDirectoryTestCase) GetStarlarkCode() string {
-	return fmt.Sprintf("%s(%s=%q)", directory.DirectoryTypeName, directory.PersistentKeyAttr, testPersistentDirectoryKey)
+	return fmt.Sprintf("%s(%s=%q, %s=%d)", directory.DirectoryTypeName, directory.PersistentKeyAttr, testPersistentDirectoryKey, directory.SizeKeyAttr, testPersistentDirectorySize)
 }
 
 func (t *directoryPersistentDirectoryTestCase) Assert(typeValue builtin_argument.KurtosisValueType) {
@@ -35,4 +35,8 @@ func (t *directoryPersistentDirectoryTestCase) Assert(typeValue builtin_argument
 	require.Nil(t, err)
 	require.True(t, found)
 	require.Equal(t, testPersistentDirectoryKey, persistentKey)
+
+	size, err := directoryStarlark.GetSizeOrDefault()
+	require.Nil(t, err)
+	require.Equal(t, testPersistentDirectorySize, size)
 }
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_full_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_full_framework_test.go
index 561aa9de2..062200f69 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_full_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_full_framework_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/directory"
 	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_constants"
 	"github.com/stretchr/testify/require"
 	"net"
 	"testing"
@@ -88,11 +89,14 @@ func (t *serviceConfigFullTestCase) Assert(typeValue builtin_argument.KurtosisVa
 	require.NotNil(t, serviceConfig.GetFilesArtifactsExpansion())
 	require.Equal(t, expectedFilesArtifactMap, serviceConfig.GetFilesArtifactsExpansion().ServiceDirpathsToArtifactIdentifiers)
 
-	expectedPersistentDirectoryMap := map[string]service_directory.DirectoryPersistentKey{
-		testPersistentDirectoryPath: service_directory.DirectoryPersistentKey(testPersistentDirectoryKey),
+	expectedPersistentDirectoryMap := map[string]service_directory.PersistentDirectory{
+		testPersistentDirectoryPath: {
+			PersistentKey: service_directory.DirectoryPersistentKey(testPersistentDirectoryKey),
+			Size:          service_directory.DirectoryPersistentSize(startosis_constants.DefaultPersistentDirectorySize),
+		},
 	}
 	require.NotNil(t, serviceConfig.GetPersistentDirectories())
-	require.Equal(t, expectedPersistentDirectoryMap, serviceConfig.GetPersistentDirectories().ServiceDirpathToDirectoryPersistentKey)
+	require.Equal(t, expectedPersistentDirectoryMap, serviceConfig.GetPersistentDirectories().ServiceDirpathToPersistentDirectory)
 
 	require.Equal(t, testEntryPointSlice, serviceConfig.GetEntrypointArgs())
 	require.Equal(t, testCmdSlice, serviceConfig.GetCmdArgs())
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go
index df2aedfa9..919d36c14 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go
@@ -48,6 +48,7 @@ var (
 	testFilesArtifactName2      = "file_2"
 	testPersistentDirectoryPath = "path/to/persistent/dir"
 	testPersistentDirectoryKey  = "persistent-dir-test"
+	testPersistentDirectorySize = int64(30)
 
 	testEntryPointSlice = []string{
 		"127.0.0.0",
diff --git a/internal_testsuites/golang/testsuite/startosis_test/startosis_test.go b/internal_testsuites/golang/testsuite/startosis_test/startosis_test.go
index e5adada60..dd61257ad 100644
--- a/internal_testsuites/golang/testsuite/startosis_test/startosis_test.go
+++ b/internal_testsuites/golang/testsuite/startosis_test/startosis_test.go
@@ -24,6 +24,7 @@ const (
 	renderedConfigRelativePath    = "foo/bar.yml"
 	renderedConfigFile            = renderedConfigMountPath + "/" + renderedConfigRelativePath
 
+	// TODO remove dependence on external package - if the rendering fails next time please make it local or put in our test packages!
 	startosisScript = `
 DATASTORE_IMAGE = "kurtosistech/example-datastore-server"
 DATASTORE_SERVICE_NAME = "` + serviceName + `"
@@ -36,7 +37,7 @@ FILE_TO_BE_CREATED = "` + fileToBeCreated + `"
 SERVICE_DEPENDENT_ON_DATASTORE_SERVICE = "` + serviceIdForDependentService + `"
 PATH_TO_MOUNT_ON_DEPENDENT_SERVICE =  "` + pathToCheckOnDependentService + `"
 
-TEMPLATE_FILE_TO_RENDER="github.com/kurtosis-tech/ethereum-package/static_files/prometheus-config/prometheus.yml.tmpl"
+TEMPLATE_FILE_TO_RENDER="github.com/kurtosis-tech/prometheus-package/static-files/prometheus.yml.tmpl"
 PATH_TO_MOUNT_RENDERED_CONFIG="` + renderedConfigMountPath + `"
 RENDER_RELATIVE_PATH = "` + renderedConfigRelativePath + `"
 
@@ -183,8 +184,7 @@ scrape_configs:
     static_configs:
       - targets: ['foobar.com']
         labels:
-          test: "pass"
-`
+          test: "pass"`
 	logrus.Infof("Checking that the file got mounted on " + serviceIdForDependentService)
 	serviceCtx, err = enclaveCtx.GetServiceContext(serviceIdForDependentService)
 	require.Nil(t, err, "Unexpected Error Creating Service Context")