---
title: Change LDAP schema
description: Learn how to change LDAP schema definitions online and offline.
component: pingds
version: 8.1
page_id: pingds:use-cases:schema
canonical_url: https://docs.pingidentity.com/pingds/8.1/use-cases/schema.html
revdate: 2025-10-22T14:42:39Z
keywords: ["LDAP", "Use Case"]
section_ids:
  description: Description
  goals: Goals
  example_scenario: Example scenario
  prerequisites: Prerequisites
  knowledge: Knowledge
  actions: Actions
  tasks: Tasks
  task_1_add_a_classofservice_index: "Task 1: Add a classOfService index"
  task_2_develop_schema_changes: "Task 2: Develop schema changes"
  task_3_save_changed_schema_files: "Task 3: Save changed schema files"
  task_4_deploy_changed_schema_files: "Task 4: Deploy changed schema files"
  task_5_deploy_the_classofservice_index: "Task 5: Deploy the classOfService index"
  validation: Validation
  whats_next: What's next
  explore_further: Explore further
  reference_material: Reference material
---

# Change LDAP schema

Learn how to change LDAP schema definitions online and offline.

## Description

Estimated time to complete: 30 minutes *(tooltip: This assumes you complete the prerequisites beforehand.)*

LDAP schema definitions determine the kinds of information in the directory and how the information is related. You can update the schema definitions online and offline to change what the directory allows.

Develop and test schema changes online to catch any errors in the updated definitions. After you validate the schema changes, you can deploy them online with the `ldapmodify` command or offline by copying updated schema files. Replication replays the LDAP schema changes to other DS servers.

In this use case, you:

* Understand a scenario where schema changes make sense.

* Understand how schema changes can require rebuilding indexes.

* Develop and test schema changes.

* Practice rolling out schema changes by copying updated schema files.

## Goals

In completing this use case, you learn to:

* Use the `ldapmodify` command to change LDAP schema.

* Rebuild indexes affected by schema changes.

* Review and remove replication metadata from changed schema files.

## Example scenario

One of the directory application owners asks Pat to let their application page through accounts by class of service.

Pat's directory deployment uses the definition for the `classOfService` attribute based on the evaluation profile.

Pat can add an index for the `classOfService` attribute, but wonders if the application owner has additional requirements. In discussion with the application owner, Pat learns the application owner:

* Found the class of service attribute can accept any random string value.

  They ask Pat if class of service could be restricted to an enumeration of `bronze`, `silver`, `gold`, and `platinum`.

* Wants a `sharedQuota` attribute like the `diskQuota` and `mailQuota` attributes.

  The application owner doesn't use `sharedQuota` yet, but plans to use it in a few weeks.

## Prerequisites

### Knowledge

Before you start:

* Make sure you are familiar with the command line on your operating system.

* If you're new to directory services, work through the [examples to learn LDAP](../getting-started/ldap.html).

### Actions

Before you try this example, install a DS server [in evaluation mode](../getting-started/install.html).

## Tasks

Pat shows the tasks with DS servers in evaluation mode. The order and content of the tasks for production deployments are the same.

### Task 1: Add a `classOfService` index

The application owner wants to page through accounts by class of service. Class of service has only a few values, and every user account could have the attribute. This is a good match for a big index.

1. Create the index:

   ```console
   $ /path/to/opendj/bin/dsconfig \
    create-backend-index \
    --backend-name dsEvaluation \
    --index-name classOfService \
    --set index-type:big-equality \
    --hostname localhost \
    --port 4444 \
    --bindDN uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin \
    --no-prompt
   ```

2. Build the new index:

   ```console
   $ /path/to/opendj/bin/rebuild-index \
    --baseDn dc=example,dc=com \
    --index classOfService \
    --hostname localhost \
    --port 4444 \
    --bindDn uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin
   ```

Applications can now use the simple paged results control to page through entries with a specified class of service.

### Task 2: Develop schema changes

Pat notices the `classOfService` attribute has `SYNTAX 1.3.6.1.4.1.1466.115.121.1.15` (directory string syntax). Pat can change the schema definition to use a custom enumeration syntax, so DS only allows applications to set one of the desired values. Pat can update the schema again to extend the enumeration as necessary.

Pat also adds a new `sharedQuota` attribute modeled on the `diskQuota` and `mailQuota` attributes.

Pat knows DS rejects malformed online modifications to schema definitions. Pat develops and tests the schema changes with the `ldapmodify` command.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | When changing a schema definition, delete the existing value and add the new value as part of the same modification. Otherwise, there's a window after you delete a definition and before you add the new one where an update could fail or an index could become untrusted due to the missing schema definition.The definition you delete must match the definition in the schema LDIF exactly, not including space characters.When you update schema definitions online, DS sets the `X-SCHEMA-FILE` value even if you don't. |

1. Update the schema definitions.

   The following example command:

   * Adds an enumeration syntax for class of service

   * Updates the `classOfService` attribute to use the enumeration syntax

   * Adds a `sharedQuota` attribute to the `cos` object class for class of service attributes

   ```console
   $ /path/to/opendj/bin/ldapmodify \
    --hostname localhost \
    --port 1636 \
    --useSsl \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin \
    --bindDN uid=admin \
    --bindPassword password << EOF
   dn: cn=schema
   changetype: modify
   add: ldapSyntaxes
   ldapSyntaxes: ( example-custom-syntax-oid
     DESC 'Enumeration syntax for class of service'
     X-ENUM ( 'bronze' 'silver' 'gold' 'platinum' )
     X-ORIGIN 'DS Documentation Examples' )
   -
   delete: attributeTypes
   attributeTypes: ( example-class-of-service-attribute-type
     NAME 'classOfService'
     EQUALITY caseIgnoreMatch
     ORDERING caseIgnoreOrderingMatch
     SUBSTR caseIgnoreSubstringsMatch
     SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
     SINGLE-VALUE
     USAGE userApplications
     X-ORIGIN 'DS Documentation Examples' )
   -
   add: attributeTypes
   attributeTypes: ( example-class-of-service-attribute-type
     NAME 'classOfService'
     EQUALITY caseIgnoreMatch
     ORDERING caseIgnoreOrderingMatch
     SUBSTR caseIgnoreSubstringsMatch
     SYNTAX example-custom-syntax-oid
     SINGLE-VALUE
     USAGE userApplications
     X-ORIGIN 'DS Documentation Examples' )
   -
   add: attributeTypes
   attributeTypes: ( example-class-of-service-shared-quota
     NAME 'sharedQuota'
     EQUALITY caseIgnoreMatch
     ORDERING caseIgnoreOrderingMatch
     SUBSTR caseIgnoreSubstringsMatch
     SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
     USAGE userApplications
     X-ORIGIN 'DS Documentation Examples' )
   -
   delete: objectClasses
   objectClasses: ( example-class-of-service-object-class
     NAME 'cos'
     SUP top
     AUXILIARY
     MAY ( classOfService $ diskQuota $ mailQuota )
     X-ORIGIN 'DS Documentation Examples' )
   -
   add: objectClasses
   objectClasses: ( example-class-of-service-object-class
     NAME 'cos'
     SUP top
     AUXILIARY
     MAY ( classOfService $ diskQuota $ mailQuota $ sharedQuota )
     X-ORIGIN 'DS Documentation Examples' )
   EOF
   ```

2. Rebuild affected indexes.

   This update changes the `classOfService` syntax, so rebuild the index to use the new syntax:

   ```console
   $ /path/to/opendj/bin/rebuild-index \
    --baseDn dc=example,dc=com \
    --index classOfService \
    --hostname localhost \
    --port 4444 \
    --bindDn uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin
   ```

   If the enumeration syntax changes again, rebuild the `classOfService` index.

### Task 3: Save changed schema files

For the production servers, Pat doesn't change the schema online. Pat keeps schema definition files under source control to track all schema changes.

After modifying the schema online, Pat locates the schema definitions added and changed in the `db/schema` LDIF files. Pat notices DS rewrites the updated LDIF files with one schema definition per line.

Before putting the changed LDIF files under source control, Pat takes care to remove the operational attributes including the `ds-sync-generation-id` and `ds-sync-state` attributes. Using the wrong values for those attributes could break schema replication. Pat lets DS replication manage the operational attributes.

In Pat's copies of the LDIF files, the schema definitions are folded for readability. Each line continuation starts with *two spaces* before a schema element keyword. LDIF continuation consumes the first space. The second space separates the keyword from the preceding text.

> **Collapse: Show**
>
> ```none
> dn: cn=schema
> objectclass: top
> objectclass: ldapSubentry
> objectclass: subschema
> cn: schema
> ldapSyntaxes: ( example-custom-syntax-oid
>   DESC 'Enumeration syntax for class of service'
>   X-ENUM ( 'bronze' 'silver' 'gold' 'platinum' )
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( example-class-of-service-disk-quota
>   NAME 'diskQuota'
>   EQUALITY caseIgnoreMatch
>   ORDERING caseIgnoreOrderingMatch
>   SUBSTR caseIgnoreSubstringsMatch
>   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
>   USAGE userApplications
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( example-class-of-service-mail-quota
>   NAME 'mailQuota'
>   EQUALITY caseIgnoreMatch
>   ORDERING caseIgnoreOrderingMatch
>   SUBSTR caseIgnoreSubstringsMatch
>   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
>   USAGE userApplications
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( example-class-of-service-shared-quota
>   NAME 'sharedQuota'
>   EQUALITY caseIgnoreMatch
>   ORDERING caseIgnoreOrderingMatch
>   SUBSTR caseIgnoreSubstringsMatch
>   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
>   USAGE userApplications
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( json-attribute-oid
>   NAME 'json'
>   EQUALITY caseIgnoreJsonQueryMatch
>   SYNTAX 1.3.6.1.4.1.36733.2.1.3.1
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( oauth2token-attribute-oid
>   NAME 'oauth2Token'
>   EQUALITY caseIgnoreOAuth2TokenQueryMatch
>   SYNTAX 1.3.6.1.4.1.36733.2.1.3.1
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( jsonToken-attribute-oid
>   NAME 'jsonToken'
>   EQUALITY caseIgnoreJsonTokenIDMatch
>   SYNTAX 1.3.6.1.4.1.36733.2.1.3.1
>   SINGLE-VALUE
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> attributeTypes: ( example-class-of-service-attribute-type
>   NAME 'classOfService'
>   EQUALITY caseIgnoreMatch
>   ORDERING caseIgnoreOrderingMatch
>   SUBSTR caseIgnoreSubstringsMatch
>   SYNTAX example-custom-syntax-oid
>   SINGLE-VALUE
>   USAGE userApplications
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> objectClasses: ( json-object-class-oid
>   NAME 'jsonObject'
>   SUP top
>   AUXILIARY
>   MAY json
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> objectClasses: ( oauth2token-object-class-oid
>   NAME 'oauth2TokenObject'
>   SUP top
>   AUXILIARY
>   MAY oauth2Token
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> objectClasses: ( json-token-object-class-oid
>   NAME 'JsonTokenObject'
>   SUP top
>   AUXILIARY
>   MAY jsonToken
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> objectClasses: ( example-class-of-service-object-class
>   NAME 'cos'
>   SUP top
>   AUXILIARY
>   MAY ( classOfService $ diskQuota $ mailQuota $ sharedQuota )
>   X-ORIGIN 'DS Documentation Examples'
>   X-SCHEMA-FILE '60-ds-evaluation-schema.ldif' )
> ```

> **Collapse: Show**
>
> ```none
> dn: cn=schema
> objectclass: top
> objectclass: ldapSubentry
> objectclass: subschema
> cn: schema
> ```

Pat also keeps copies of the original DS schema files under source control. When upgrading, Pat compares the original files with the upgraded files and applies any changes to the modified production files as necessary.

### Task 4: Deploy changed schema files

To make a schema change in deployment, stop the server, add the custom schema, and restart the server.

1. Prepare to show schema change deployment by setting up two replicated DS directory servers as described in [Install DS](../getting-started/install.html) and [Learn replication](../getting-started/replication.html).

2. Make sure you have local copies of the changed schema definition files:

   * [60-ds-evaluation-schema.ldif](../_attachments/ldif/60-ds-evaluation-schema.ldif)

     This file contains the changed schema definitions.

   * [99-user.ldif](../_attachments/ldif/99-user.ldif)

     This file removes the replication metadata.

3. Stop a server:

   ```console
   $ /path/to/opendj/bin/stop-ds
   ```

4. Add the custom schema files and start the replica:

   ```console
   $ cp 60-ds-evaluation-schema.ldif /path/to/opendj/db/schema/
   $ cp 99-user.ldif /path/to/opendj/db/schema/
   ```

5. Start the server:

   ```console
   $ /path/to/opendj/bin/start-ds
   ```

Replication applies the changes to other servers.

### Task 5: Deploy the `classOfService` index

Create and build the index on each replica an application uses for searches:

1. Create the index on the first server:

   ```console
   $ /path/to/opendj/bin/dsconfig \
    create-backend-index \
    --backend-name dsEvaluation \
    --index-name classOfService \
    --set index-type:big-equality \
    --hostname localhost \
    --port 4444 \
    --bindDN uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin \
    --no-prompt
   ```

2. Build the new index on the first server:

   ```console
   $ /path/to/opendj/bin/rebuild-index \
    --baseDn dc=example,dc=com \
    --index classOfService \
    --hostname localhost \
    --port 4444 \
    --bindDn uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/opendj/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/opendj/config/keystore.pin
   ```

3. Create the index on the second server:

   ```console
   $ /path/to/replica/bin/dsconfig \
    create-backend-index \
    --backend-name dsEvaluation \
    --index-name classOfService \
    --set index-type:big-equality \
    --hostname localhost \
    --port 14444 \
    --bindDN uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/replica/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/replica/config/keystore.pin \
    --no-prompt
   ```

4. Build the new index on the second server:

   ```console
   $ /path/to/replica/bin/rebuild-index \
    --baseDn dc=example,dc=com \
    --index classOfService \
    --hostname localhost \
    --port 14444 \
    --bindDn uid=admin \
    --bindPassword password \
    --trustStorePath /path/to/replica/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/replica/config/keystore.pin
   ```

The new schema definitions and indexes are ready to use.

## Validation

After you deploy the changed schema definitions and `classOfService` indexes, follow these steps to check you can use the updated schema definitions and index.

1. Page through entries with `gold` class of service on the second replica as a user who doesn't have the `unindexed-search` privilege:

   ```console
   $ ldapsearch \
    --hostname localhost \
    --port 11636 \
    --useSsl \
    --trustStorePath /path/to/replica/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/replica/config/keystore.pin \
    --bindDN uid=kvaughan,ou=People,dc=example,dc=com \
    --bindPassword bribery \
    --baseDn dc=example,dc=com \
    --simplePageSize 5 \
    "(classOfService=gold)" \
    mail
   ```

   > **Collapse: Show output**
   >
   > ```
   > dn: uid=abarnes,ou=People,dc=example,dc=com
   > mail: abarnes@example.com
   >
   > dn: uid=ahall,ou=People,dc=example,dc=com
   > mail: ahall@example.com
   >
   > dn: uid=aknutson,ou=People,dc=example,dc=com
   > mail: aknutson@example.com
   >
   > dn: uid=alutz,ou=People,dc=example,dc=com
   > mail: alutz@example.com
   >
   > dn: uid=ashelton,ou=People,dc=example,dc=com
   > mail: ashelton@example.com
   >
   > Press RETURN to continue
   > ```

2. Show users can now have `platinum` class of service:

   ```console
   $ /path/to/replica/bin/ldapmodify \
    --hostname localhost \
    --port 11636 \
    --useSsl \
    --trustStorePath /path/to/replica/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/replica/config/keystore.pin \
    --bindDN uid=admin \
    --bindPassword password << EOF
   dn: uid=bjensen,ou=People,dc=example,dc=com
   changetype: modify
   replace: classOfService
   classOfService: platinum
   EOF
   ```

3. Show users can't have a random string for class of service:

   ```console
   $ /path/to/replica/bin/ldapmodify \
    --hostname localhost \
    --port 11636 \
    --useSsl \
    --trustStorePath /path/to/replica/config/keystore \
    --trustStoreType PKCS12 \
    --trustStorePassword:file /path/to/replica/config/keystore.pin \
    --bindDN uid=admin \
    --bindPassword password << EOF
   dn: uid=bjensen,ou=People,dc=example,dc=com
   changetype: modify
   replace: classOfService
   classOfService: custom extended service
   EOF
   ```

   > **Collapse: Show output**
   >
   > ```
   > # The LDAP modify request failed: 21 (Invalid Attribute Syntax)
   > # Additional Information:  When attempting to modify entry uid=bjensen,ou=People,dc=example,dc=com to replace the set of values for attribute classOfService, value "custom extended service" was found to be invalid according to the associated syntax: The provided value "custom extended service" cannot be parsed because it is not allowed by enumeration syntax with OID "example-custom-syntax-oid"
   > ```

## What's next

Pat knows schema definition changes are safe in files under source control. The *reasons* for the schema changes are not so well known. Pat plans to start and maintain a schema dictionary. The schema dictionary will describe each attribute known to be in use. It will track:

* Who uses the attribute, including their contact information, and how they use it

* What data it stores, and who owns the data, including contact information

* Where the data comes from, especially if it comes from another system

* When there are maintenance windows for the attribute (for reindexing and so on)

In addition, Pat has more to discuss with the application owner, who asked for the new `sharedQuota` attribute. The `diskQuota` and `mailQuota` attributes [depend on the `classOfService` attribute](../config-guide/collective-attrs.html#example-collective-attrs-cos) for their values.

* How should DS define `sharedQuota` values?

* What should the quotas be for `classOfService: platinum`?

## Explore further

### Reference material

| Reference                                                     | Description                                                 |
| ------------------------------------------------------------- | ----------------------------------------------------------- |
| [Indexes](../config-guide/indexing.html)                      | Background and how-to instructions for working with indexes |
| [LDAP schema](../config-guide/schema.html)                    | An in-depth look at LDAP schema definitions                 |
| [LDAP schema](../ldap-guide/schema.html)                      | LDAP schema in client applications                          |
| [JSON schema](../rest-guide/rest-operations.html#hdap-schema) | Schema for HTTP client applications                         |
| [About This Reference](../schemaref/preface.html)             | A reference for all default schema definitions              |
