diff --git a/cmd/policy/main.go b/cmd/policy/main.go
index df96345889461bb0379e4c6653cc8cfac345eeaf..faf2bbb469e5ee5727d923c9c7c224e4c76aa6e9 100644
--- a/cmd/policy/main.go
+++ b/cmd/policy/main.go
@@ -166,7 +166,7 @@ func main() {
 		healthSvc goahealth.Service
 	)
 	{
-		policySvc = policy.New(storage, regocache, cache, signer, logger)
+		policySvc = policy.New(storage, regocache, cache, signer, cfg.Policy.LockOnValidationFailure, logger)
 		healthSvc = health.New(Version)
 	}
 
diff --git a/internal/config/config.go b/internal/config/config.go
index eaaaadb782e8a4cda17c4056dacbfadc9b6114bc..9bbca888ca4cebab476cfe9b8db146d4259daa3d 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -64,6 +64,10 @@ type policyConfig struct {
 	// needed policies. If present, only policies inside this folder
 	// are going to be fetched and used for evaluation.
 	Folder string `envconfig:"POLICY_REPOSITORY_FOLDER"`
+
+	// LockOnValidationFailure indicates whether a policy must be locked for execution
+	// if the policy output fails the schema validation.
+	LockOnValidationFailure bool `envconfig:"POLICY_LOCK_ON_VALIDATION_FAILURE" default:"false"`
 }
 
 type metricsConfig struct {
diff --git a/internal/service/policy/bundle_test.go b/internal/service/policy/bundle_test.go
index b32e12c2b7bd6c02f2eab81507000c3dcbd99e70..2ebbfd336f1e5481f644295c97cbab6531175e54 100644
--- a/internal/service/policy/bundle_test.go
+++ b/internal/service/policy/bundle_test.go
@@ -47,7 +47,7 @@ var testMetadata = Metadata{
 }
 
 func TestPolicy_createPolicyBundle(t *testing.T) {
-	svc := New(nil, nil, nil, nil, zap.NewNop())
+	svc := New(nil, nil, nil, nil, false, zap.NewNop())
 	bundle, err := svc.createPolicyBundle(testPolicy)
 	assert.NoError(t, err)
 	assert.NotNil(t, bundle)
diff --git a/internal/service/policy/service.go b/internal/service/policy/service.go
index bcb7a36f701e6c5c4c3b1432c14401b519c19fc0..d4728ad86765eec74c1f0d5d784bfb8eb5565812 100644
--- a/internal/service/policy/service.go
+++ b/internal/service/policy/service.go
@@ -44,14 +44,16 @@ type Signer interface {
 }
 
 type Service struct {
-	storage     Storage
-	policyCache RegoCache
-	cache       Cache
-	signer      Signer
-	logger      *zap.Logger
+	storage        Storage
+	policyCache    RegoCache
+	cache          Cache
+	signer         Signer
+	validationLock bool
+
+	logger *zap.Logger
 }
 
-func New(storage Storage, policyCache RegoCache, cache Cache, signer Signer, logger *zap.Logger) *Service {
+func New(storage Storage, policyCache RegoCache, cache Cache, signer Signer, validationLock bool, logger *zap.Logger) *Service {
 	signerFactory := func(signer Signer) func() (jws.Signer, error) {
 		return func() (jws.Signer, error) {
 			return &signAdapter{signer: signer}, nil
@@ -70,11 +72,12 @@ func New(storage Storage, policyCache RegoCache, cache Cache, signer Signer, log
 	jws.RegisterSigner(JwaVaultSignature, jws.SignerFactoryFn(signerFactory(signer)))
 
 	return &Service{
-		storage:     storage,
-		policyCache: policyCache,
-		cache:       cache,
-		signer:      signer,
-		logger:      logger,
+		storage:        storage,
+		policyCache:    policyCache,
+		cache:          cache,
+		signer:         signer,
+		validationLock: validationLock,
+		logger:         logger,
 	}
 }
 
@@ -205,8 +208,15 @@ func (s *Service) Validate(ctx context.Context, req *policy.EvaluateRequest) (*p
 
 	// validate the policy output
 	if err := sch.Validate(res.Result); err != nil {
-		logger.Error("invalid policy output schema", zap.Error(err))
-		return nil, errors.New("invalid policy output schema", err)
+		// lock the policy for execution if configured
+		if s.validationLock {
+			if err := s.lock(ctx, pol); err != nil {
+				logger.Error("error locking policy after validation failure", zap.Error(err))
+			}
+		}
+
+		logger.Error("policy output schema validation failed", zap.Error(err))
+		return nil, errors.New(errors.Unknown, "policy output schema validation failed", err)
 	}
 
 	return res, nil
@@ -231,17 +241,25 @@ func (s *Service) Lock(ctx context.Context, req *policy.LockRequest) error {
 		return errors.New("error locking policy", err)
 	}
 
-	if pol.Locked {
+	if err := s.lock(ctx, pol); err != nil {
+		logger.Error("error locking policy", zap.Error(err))
+		return err
+	}
+
+	logger.Debug("policy is locked")
+
+	return nil
+}
+
+func (s *Service) lock(ctx context.Context, p *storage.Policy) error {
+	if p.Locked {
 		return errors.New(errors.Forbidden, "policy is already locked")
 	}
 
-	if err := s.storage.SetPolicyLock(ctx, req.Repository, req.Group, req.PolicyName, req.Version, true); err != nil {
-		logger.Error("error locking policy", zap.Error(err))
+	if err := s.storage.SetPolicyLock(ctx, p.Repository, p.Group, p.Name, p.Version, true); err != nil {
 		return errors.New("error locking policy", err)
 	}
 
-	logger.Debug("policy is locked")
-
 	return nil
 }
 
diff --git a/internal/service/policy/service_test.go b/internal/service/policy/service_test.go
index 43c1ff736952b64dba389da54339ed068121367b..59a5d47ee55d1c55c0a2df62898f18d0f5daaae1 100644
--- a/internal/service/policy/service_test.go
+++ b/internal/service/policy/service_test.go
@@ -26,10 +26,25 @@ import (
 )
 
 func TestNew(t *testing.T) {
-	svc := policy.New(nil, nil, nil, nil, zap.NewNop())
+	svc := policy.New(nil, nil, nil, nil, false, zap.NewNop())
 	assert.Implements(t, (*goapolicy.Service)(nil), svc)
 }
 
+// testReq prepares test request to be used in tests
+func testReq() *goapolicy.EvaluateRequest {
+	input := map[string]interface{}{"msg": "yes"}
+	var body interface{} = input
+
+	return &goapolicy.EvaluateRequest{
+		Repository: "policies",
+		Group:      "testgroup",
+		PolicyName: "example",
+		Version:    "1.0",
+		Input:      &body,
+		TTL:        ptr.Int(30),
+	}
+}
+
 func TestService_Evaluate(t *testing.T) {
 	testPolicy := &storage.Policy{
 		Repository: "policies",
@@ -52,21 +67,6 @@ func TestService_Evaluate(t *testing.T) {
 	// prepare test policy accessing headers during evaluation
 	testPolicyAccessingHeaders := `package testgroup.example token := external.http.header("Authorization")`
 
-	// prepare test request to be used in tests
-	testReq := func() *goapolicy.EvaluateRequest {
-		input := map[string]interface{}{"msg": "yes"}
-		var body interface{} = input
-
-		return &goapolicy.EvaluateRequest{
-			Repository: "policies",
-			Group:      "testgroup",
-			PolicyName: "example",
-			Version:    "1.0",
-			Input:      &body,
-			TTL:        ptr.Int(30),
-		}
-	}
-
 	// prepare test request with empty body
 	testEmptyReq := func() *goapolicy.EvaluateRequest {
 		var body interface{}
@@ -373,7 +373,7 @@ func TestService_Evaluate(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			svc := policy.New(test.storage, test.regocache, test.cache, nil, zap.NewNop())
+			svc := policy.New(test.storage, test.regocache, test.cache, nil, false, zap.NewNop())
 			ctx := context.Background()
 			if test.ctx != nil {
 				ctx = test.ctx
@@ -397,6 +397,227 @@ func TestService_Evaluate(t *testing.T) {
 	}
 }
 
+func TestService_Validate(t *testing.T) {
+	// prepare basic JSON schema
+	jsonSchema := `
+		{
+		  "type": "object",
+		  "properties": {
+			"foo": {
+			  "type": "string",
+			  "minLength": 5
+			}
+		  },
+		  "required": [
+			"foo"
+		  ]
+		}
+	`
+	// prepare schema with specified $schema property
+	jsonSchemaWithSchemaProperty := `
+		{
+		  "$schema": "http://json-schema.org/draft-04/schema#",
+		  "type": "object",
+		  "properties": {
+			"foo": {
+			  "type": "string",
+			  "minLength": 5
+			}
+		  },
+		  "required": [
+			"foo"
+		  ]
+		}
+	`
+
+	tests := []struct {
+		name      string
+		req       *goapolicy.EvaluateRequest
+		storage   policy.Storage
+		regocache policy.RegoCache
+		cache     policy.Cache
+		// expected result
+		evalRes *goapolicy.EvaluateResult
+		errkind errors.Kind
+		errtext string
+	}{
+		{
+			name: "output validation schema is empty",
+			req:  testReq(),
+			regocache: &policyfakes.FakeRegoCache{
+				GetStub: func(key string) (*storage.Policy, bool) {
+					return nil, false
+				},
+			},
+			storage: &policyfakes.FakeStorage{
+				PolicyStub: func(ctx context.Context, s string, s2 string, s3 string, s4 string) (*storage.Policy, error) {
+					return &storage.Policy{
+						Repository:   "policies",
+						Name:         "example",
+						Group:        "testgroup",
+						Version:      "1.0",
+						Rego:         `package testgroup.example _ = {"hello":"world"}`,
+						Locked:       false,
+						OutputSchema: "",
+						LastUpdate:   time.Now(),
+					}, nil
+				},
+			},
+			cache: &policyfakes.FakeCache{
+				SetStub: func(ctx context.Context, s string, s2 string, s3 string, bytes []byte, i int) error {
+					return nil
+				},
+			},
+			errtext: "validation schema for policy output is not found",
+			errkind: errors.BadRequest,
+		},
+		{
+			name: "output validation schema is invalid JSON schema",
+			req:  testReq(),
+			regocache: &policyfakes.FakeRegoCache{
+				GetStub: func(key string) (*storage.Policy, bool) {
+					return nil, false
+				},
+			},
+			storage: &policyfakes.FakeStorage{
+				PolicyStub: func(ctx context.Context, s string, s2 string, s3 string, s4 string) (*storage.Policy, error) {
+					return &storage.Policy{
+						Repository:   "policies",
+						Name:         "example",
+						Group:        "testgroup",
+						Version:      "1.0",
+						Rego:         `package testgroup.example _ = {"hello":"world"}`,
+						Locked:       false,
+						OutputSchema: "invalid JSON schema",
+						LastUpdate:   time.Now(),
+					}, nil
+				},
+			},
+			cache: &policyfakes.FakeCache{
+				SetStub: func(ctx context.Context, s string, s2 string, s3 string, bytes []byte, i int) error {
+					return nil
+				},
+			},
+			errtext: "error compiling output validation schema",
+			errkind: errors.Unknown,
+		},
+		{
+			name: "policy output schema validation fails",
+			req:  testReq(),
+			regocache: &policyfakes.FakeRegoCache{
+				GetStub: func(key string) (*storage.Policy, bool) {
+					return nil, false
+				},
+			},
+			storage: &policyfakes.FakeStorage{
+				PolicyStub: func(ctx context.Context, s string, s2 string, s3 string, s4 string) (*storage.Policy, error) {
+					return &storage.Policy{
+						Repository:   "policies",
+						Name:         "example",
+						Group:        "testgroup",
+						Version:      "1.0",
+						Rego:         `package testgroup.example _ = {"foo":"bar"}`,
+						Locked:       false,
+						OutputSchema: jsonSchema,
+						LastUpdate:   time.Now(),
+					}, nil
+				},
+			},
+			cache: &policyfakes.FakeCache{
+				SetStub: func(ctx context.Context, s string, s2 string, s3 string, bytes []byte, i int) error {
+					return nil
+				},
+			},
+			errtext: "policy output schema validation failed",
+			errkind: errors.Unknown,
+		},
+		{
+			name: "policy output validation is successful",
+			req:  testReq(),
+			regocache: &policyfakes.FakeRegoCache{
+				GetStub: func(key string) (*storage.Policy, bool) {
+					return nil, false
+				},
+			},
+			storage: &policyfakes.FakeStorage{
+				PolicyStub: func(ctx context.Context, s string, s2 string, s3 string, s4 string) (*storage.Policy, error) {
+					return &storage.Policy{
+						Repository:   "policies",
+						Name:         "example",
+						Group:        "testgroup",
+						Version:      "1.0",
+						Rego:         `package testgroup.example _ = {"foo":"barbaz"}`,
+						Locked:       false,
+						OutputSchema: jsonSchema,
+						LastUpdate:   time.Now(),
+					}, nil
+				},
+			},
+			cache: &policyfakes.FakeCache{
+				SetStub: func(ctx context.Context, s string, s2 string, s3 string, bytes []byte, i int) error {
+					return nil
+				},
+			},
+			evalRes: &goapolicy.EvaluateResult{
+				Result: map[string]interface{}{"foo": "barbaz"},
+			},
+		},
+		{
+			name: "policy output validation using explicit schema draft version is successful ",
+			req:  testReq(),
+			regocache: &policyfakes.FakeRegoCache{
+				GetStub: func(key string) (*storage.Policy, bool) {
+					return nil, false
+				},
+			},
+			storage: &policyfakes.FakeStorage{
+				PolicyStub: func(ctx context.Context, s string, s2 string, s3 string, s4 string) (*storage.Policy, error) {
+					return &storage.Policy{
+						Repository:   "policies",
+						Name:         "example",
+						Group:        "testgroup",
+						Version:      "1.0",
+						Rego:         `package testgroup.example _ = {"foo":"barbaz"}`,
+						Locked:       false,
+						OutputSchema: jsonSchemaWithSchemaProperty,
+						LastUpdate:   time.Now(),
+					}, nil
+				},
+			},
+			cache: &policyfakes.FakeCache{
+				SetStub: func(ctx context.Context, s string, s2 string, s3 string, bytes []byte, i int) error {
+					return nil
+				},
+			},
+			evalRes: &goapolicy.EvaluateResult{
+				Result: map[string]interface{}{"foo": "barbaz"},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			svc := policy.New(test.storage, test.regocache, test.cache, nil, false, zap.NewNop())
+
+			res, err := svc.Validate(context.Background(), test.req)
+			if err == nil {
+				assert.Empty(t, test.errtext)
+				assert.NotNil(t, res)
+
+				assert.Equal(t, test.evalRes.Result, res.Result)
+				assert.NotEmpty(t, res.ETag)
+			} else {
+				e, ok := err.(*errors.Error)
+				assert.True(t, ok)
+
+				assert.Contains(t, e.Error(), test.errtext)
+				assert.Equal(t, test.errkind, e.Kind)
+				assert.Equal(t, test.evalRes, res)
+			}
+		})
+	}
+}
+
 func TestService_Lock(t *testing.T) {
 	// prepare test request to be used in tests
 	testReq := func() *goapolicy.LockRequest {
@@ -479,7 +700,7 @@ func TestService_Lock(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			svc := policy.New(test.storage, nil, nil, nil, zap.NewNop())
+			svc := policy.New(test.storage, nil, nil, nil, false, zap.NewNop())
 			err := svc.Lock(context.Background(), test.req)
 			if err == nil {
 				assert.Empty(t, test.errtext)
@@ -577,7 +798,7 @@ func TestService_Unlock(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			svc := policy.New(test.storage, nil, nil, nil, zap.NewNop())
+			svc := policy.New(test.storage, nil, nil, nil, false, zap.NewNop())
 			err := svc.Unlock(context.Background(), test.req)
 			if err == nil {
 				assert.Empty(t, test.errtext)
@@ -809,7 +1030,7 @@ func TestService_ListPolicies(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			svc := policy.New(test.storage, nil, nil, nil, zap.NewNop())
+			svc := policy.New(test.storage, nil, nil, nil, false, zap.NewNop())
 			result, err := svc.ListPolicies(context.Background(), test.request)
 
 			if test.errText != "" {
@@ -874,7 +1095,7 @@ func TestService_SubscribeForPolicyChange(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			svc := policy.New(test.storage, nil, nil, nil, zap.NewNop())
+			svc := policy.New(test.storage, nil, nil, nil, false, zap.NewNop())
 			res, err := svc.SubscribeForPolicyChange(context.Background(), test.request)
 			if test.errText != "" {
 				assert.ErrorContains(t, err, test.errText)
@@ -894,7 +1115,7 @@ func TestService_ExportBundleError(t *testing.T) {
 				return nil, errors.New(errors.NotFound, "policy not found")
 			},
 		}
-		svc := policy.New(storage, nil, nil, nil, zap.NewNop())
+		svc := policy.New(storage, nil, nil, nil, false, zap.NewNop())
 		res, reader, err := svc.ExportBundle(context.Background(), &goapolicy.ExportBundleRequest{})
 		assert.Nil(t, res)
 		assert.Nil(t, reader)
@@ -911,7 +1132,7 @@ func TestService_ExportBundleError(t *testing.T) {
 				return nil, errors.New("unexpected error")
 			},
 		}
-		svc := policy.New(storage, nil, nil, nil, zap.NewNop())
+		svc := policy.New(storage, nil, nil, nil, false, zap.NewNop())
 		res, reader, err := svc.ExportBundle(context.Background(), &goapolicy.ExportBundleRequest{})
 		assert.Nil(t, res)
 		assert.Nil(t, reader)
@@ -945,7 +1166,7 @@ func TestService_ExportBundleError(t *testing.T) {
 			},
 		}
 
-		svc := policy.New(storage, nil, nil, signer, zap.NewNop())
+		svc := policy.New(storage, nil, nil, signer, false, zap.NewNop())
 		res, reader, err := svc.ExportBundle(context.Background(), &goapolicy.ExportBundleRequest{})
 		assert.Nil(t, res)
 		assert.Nil(t, reader)
@@ -977,7 +1198,7 @@ func TestService_ExportBundleSuccess(t *testing.T) {
 		},
 	}
 
-	svc := policy.New(storage, nil, nil, signer, zap.NewNop())
+	svc := policy.New(storage, nil, nil, signer, false, zap.NewNop())
 	res, reader, err := svc.ExportBundle(context.Background(), &goapolicy.ExportBundleRequest{})
 	require.NoError(t, err)
 	require.NotNil(t, res)