Learn how to deploy 1Password SCIM Bridge on a server or virtual machine using Docker Engine.
This example describes how to deploy 1Password SCIM Bridge as a stack in a single-node Docker Swarm. The base stack includes two services (one each for the SCIM bridge container and the required Redis cache) and a Docker secret for the scimsession credentials. The server is configured to receive traffic directly from the public internet with Docker acting as a reverse proxy to the SCIM bridge container with a TLS certificate from Let's Encrypt. Other common configurations are also included for convenience.
Review the Preparation Guide at the root of this repository. The open source Docker Engine tooling can be used with any supported Linux distribution. AMD64 or ARM64 architecture is required by the SCIM bridge container image. The base configuration requires at least 0.5 vCPU and 1 GB RAM for the container workloads, plus additional overhead for the operating system and Docker Engine.
Create a public DNS record (for example, op-scim-bridge.example.com) that points to a publicly available endpoint for the server.
Connect to the machine that you will be using as the host for your SCIM bridge to set up Docker:
Tip
🔑 You can use use the 1Password SSH agent to authenticate the connection to the server (and other SSH workflows).
-
If you haven't already done so, install Docker Engine on the server (Docker Desktop is not required or expected).
-
Follow the post-install steps in the documentation to enable running Docker as a non-root user and ensure that Docker Engine starts when the server boots.
-
Run this command to create a swarm with a single node:
docker swarm init --advertise-addr 127.0.0.1
-
Clone this repository and switch to this directory:
git clone https://github.com/1Password/scim-examples.git cd ./scim-examples/docker
-
On the computer where you downloaded the file from the Automated User Provisioning setup, copy the
scimsessioncredentials file to the working directory on your server. For example, using SCP:scp ./scimsession op-scim-bridge.example.com:scim-examples/docker/scimsession
-
On the server, open
scim.envin your favourite text editor. Set the value ofOP_TLS_DOMAINto the fully qualififed domain name of the public DNS record for your SCIM bridge created in Before you begin). For example:# ... OP_TLS_DOMAIN=op-scim-bridge.example.com # ...
Save the file.
Use the Compose template to output a canonical configuration for use with Docker Swarm and create the stack from this configuration inline. Your SCIM bridge should automatically acquire and manage a TLS certificate from Let's Encrypt on your behalf:
docker stack config --compose-file ./compose.template.yaml | docker stack deploy --compose-file - op-scim-bridgeRun this command on the server to view logs from the service for the SCIM bridge container:
docker service logs op-scim-bridge_scim --rawYour SCIM bridge URL is based on the fully qualified domain name of the DNS record created in Before you begin. For example: https://op-scim-bridge.example.com. You can sign with your bearer token at your SCIM bridge URL in a web browser to view status information and download log files.
You can also check the status of your SCIM bridge with a bearer token authenticated request to its /health endpoint. Depending on your DNS and networking configuration, you may need to send the request from another device (for example, your computer) to reach the SCIM bridge URL:
-
Replace
mF_9.B5f-4.1JqMwith your bearer token andhttps://op-scim-bridge.example.comwith your SCIM bridge URL to store them as environment variable values for the current terminal session:# The below commmand includes a prepended space so that the bearer token is not saved to your terminal history export OP_SCIM_TOKEN="mF_9.B5f-4.1JqM" export OP_SCIM_BRIDGE_URL="https://op-scim-bridge.example.com"
-
Use the environment variables in your request. For example, using
curl:curl --silent --show-error --request GET --header "Accept: application/json" \ --header "Authorization: Bearer $OP_SCIM_TOKEN" $OP_SCIM_BRIDGE_URL/health
Tip
If you saved your bearer token to your 1Password account, you can use secret reference
syntax with 1Password
CLI to securely pass it inline. For example, using op read:
export OP_SCIM_TOKEN="op://Employee/Bearer Token/credential"
export OP_SCIM_BRIDGE_URL="https:/op-scim-bridge.example.com"
curl --silent --show-error --request GET --header "Accept: application/json" \
--header "Authorization: Bearer $(op read $OP_SCIM_TOKEN)" $OP_SCIM_BRIDGE_URL/healthExample JSON response:
{
"build": "209141",
"version": "2.9.14",
"reports": [
{
"source": "ConfirmationWatcher",
"time": "2026-03-03T14:06:09Z",
"expires": "2026-03-03T14:16:09Z",
"state": "healthy"
},
{
"source": "RedisCache",
"time": "2026-03-03T14:06:09Z",
"expires": "2026-03-03T14:16:09Z",
"state": "healthy"
},
{
"source": "SCIMServer",
"time": "2026-03-03T14:06:56Z",
"expires": "2026-03-03T14:16:56Z",
"state": "healthy"
},
{
"source": "StartProvisionWatcher",
"time": "2026-03-03T14:06:09Z",
"expires": "2026-03-03T14:16:09Z",
"state": "healthy"
}
],
"retrievedAt": "2026-03-03T14:06:56Z"
}[!WARNING] > If Google Workspace is your identity provider, additional steps are required: connect your 1Password SCIM Bridge to Google Workspace.
To finish setting up automated user provisioning, use your SCIM bridge URL and bearer token to connect your identity provider to 1Password SCIM Bridge.
Quarterly review and maintenance is recommended to update the operating system, the packages for Docker Engine, and the container image for 1Password SCIM Bridge.
To use a new version of SCIM bridge, update the op-scim-bridge_scim service with the new image tag from the 1password/scim repository on Docker Hub:
docker service update op-scim-bridge_scim --image 1password/scim:v2.9.14Your SCIM bridge should automatically reboot using the specified version, typically in a few seconds.
You can find details about the changes in each release of 1Password SCIM Bridge on our Release Notes website. The most recent version should be pinned in the compose.template.yaml file (and in the command above in this file) in the main branch of this repository.
Docker secrets are immutable and cannot be removed while in use by a Swarm service. To use new secret values in your stack, you must unmount the existing secret from the service configuration, remove the current Docker secret, and redeploy the stack to create and mount a new secret with the regenerated credentials:
-
Pause provisioning in your identity provider.
-
On the server where your SCIM bridge is deployed, update the
op-scim-bridge_scimservice definition to unnmount the thecredentialsDocker secret:docker service update op-scim-bridge_scim --secret-rm credentials
-
Remove the secret from the swarm:
docker secret rm credentials
-
Copy the regenerated
scimsessionfile from your 1Password account to the working directory on the server (e.g.~/scim-examples/docker). -
Switch to the working directory on the server. Run the same command used to deploy SCIM bridge to create and mount a new secret using the new file:
docker stack config --compose-file ./compose.template.yaml | docker stack deploy --compose-file - op-scim-bridge -
Test your SCIM bridge using the new bearer token associated with the regenerated
scimsessionfile. Update your identity provider configuration with the new bearer token.
A similar process can be used to update the values for any other Docker secrets used in your configuration (e.g. when using a self-managed TLS certificate).
SCIM bridge configuration can be consumed from the container environment when the container starts.
The scimsession credentials file and any other files that are consumed as Docker secrets should not be committed to source control. However, the scim.env file, included YAML templates, and your customizations are suitable as a base for your own upstream SCIM bridge repository for your deployment.
Since swarm mode in Docker Engine uses a declarative service model, you can also apply changes to update the configuration "on the fly" from your terminal for development or debugging. Services will automatically restart tasks when their configuration is updated.
For example, to reboot your SCIM bridge with debug logging enabled:
docker service update op-scim-bridge_scim --env-add OP_DEBUG=1To turn off debug logging and inject some colour into the logs in your console:
docker service update op-scim-bridge_scim \
--env-rm OP_DEBUG \
--env-add OP_PRETTY_LOGS=1Multiple Compose files can be merged when deploying or updating a stack to modify the base configuration. Several override Compose files are included in the working folder for your convenience.
The SCIM bridge container should be vertically scaled when provisioning a large number of users or groups. The Redis container resource specifications do not need to be adjusted. Our default resource specifications and recommended configurations for provisioning at scale are listed in the below table:
| Volume | Number of users | CPU | memory |
|---|---|---|---|
| Default | <1,000 | 0.125 | 512M |
| High | 1,000–5,000 | 0.5 | 1024M |
| Very high | >5,000 | 1.0 | 1024M |
If provisioning more than 1,000 users, the resources assigned to the SCIM bridge container should be updated as recommended in the above table.
Resources for the SCIM bridge container are defined in the base Compose file:
services:
# ...
scim:
# ...
deploy:
resources:
reservations:
cpus: "0.125"
limits:
memory: 512M
# ...When provisioning up to 5,000 users, merge the configuration from the compose.high-volume.yaml file:
docker stack config --compose-file ./compose.template.yaml \
--compose-file ./compose.high-volume.yaml |
docker stack deploy --compose-file - op-scim-bridgeWhen provisioning more than 5,000 users, merge the configuration from compose.very-high-volume.yaml:
docker stack config --compose-file ./compose.template.yaml \
--compose-file ./compose.very-high-volume.yaml |
docker stack deploy --compose-file - op-scim-bridgePlease reach out to our support team if you need help with the configuration or to tweak the values for your deployment.
All supported identity providers strictly require an HTTPS endpoint with a valid TLS certificate for the SCIM bridge URL. 1Password SCIM Bridge includes an optional CertificateManager component that terminates TLS traffic at the SCIM bridge container. By default, CertificateManager integrates with Let's Encrypt to acquire and renew TLS certificates. This strictly requires port 443 on the server to be widely available to any IP on the public internet (i.e., ingress allowed by 0.0.0.0) so that Let's Encrypt can initiate an inbound connection to your SCIM bridge for validation on request or renewal.
Other supported options include:
To terminate TLS traffic at another public endpoint and redirect traffic within a trusted network, SCIM bridge can be configured to disable the CertificateManager component and receive plain-text HTTP traffic on port 80 of the server. CertificateManager will be enabled if a value is set for the OP_TLS_DOMAIN variable, so any value set in scim.env must be removed (or this line must be commented out). The included compose.http.yaml file can be used to set up the port mapping when deploying (or redeploying) SCIM bridge in this configuration:
docker stack config \
--compose-file ./compose.template.yaml \
--compose-file ./compose.http.yaml |
docker stack deploy --compose-file - op-scim-bridgeYou may also choose to supply your own TLS certificate with CertificateManager instead of invoking Let's Encrypt. The value set for OP_TLS_DOMAIN must match the common name of the certificate.
Save the public certificate along with any provided intermediate certificates from your certificate authority to the file certificate.pem, and the private key in key.pem.
The provided intermediate(s) from your certificate authority must be after the leaf cert that applies to your SCIM bridge deployment URL.
Important
Without the intermediate(s) included, some identity providers (ex. Okta) will be unable to validate your TLS certificate for your SCIM bridge.
An example certificate.pem file
-----BEGIN CERTIFICATE----- # The certificate for example.com
MIIFmzCCBSGgAwIBAgIQCtiTuvposLf7ekBPBuyvmjAKBggqhkjOPQQDAzBZMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTMwMQYDVQQDEypEaWdp
Q2VydCBHbG9iYWwgRzMgVExTIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjUwMTE1
MDAwMDAwWhcNMjYwMTE1MjM1OTU5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMTwwOgYDVQQKEzNJbnRl
cm5ldCBDb3Jwb3JhdGlvbiBmb3IgQXNzaWduZWQgTmFtZXMgYW5kIE51bWJlcnMx
FjAUBgNVBAMMDSouZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AASaSJeELWFsCMlqFKDIOIDmAMCH+plXDhsA4tiHklfnCPs8XrDThCg3wSQRjtMg
cXS9k49OCQPOAjuw5GZzz6/uo4IDkzCCA48wHwYDVR0jBBgwFoAUiiPrnmvX+Tdd
+W0hOXaaoWfeEKgwHQYDVR0OBBYEFPDBajIN7NrH6o/NDW0ZElnRvnLtMCUGA1Ud
EQQeMByCDSouZXhhbXBsZS5jb22CC2V4YW1wbGUuY29tMD4GA1UdIAQ3MDUwMwYG
Z4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
UzAOBgNVHQ8BAf8EBAMCA4gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MIGfBgNVHR8EgZcwgZQwSKBGoESGQmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
aWdpQ2VydEdsb2JhbEczVExTRUNDU0hBMzg0MjAyMENBMS0yLmNybDBIoEagRIZC
aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsRzNUTFNFQ0NT
SEEzODQyMDIwQ0ExLTIuY3JsMIGHBggrBgEFBQcBAQR7MHkwJAYIKwYBBQUHMAGG
GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBRBggrBgEFBQcwAoZFaHR0cDovL2Nh
Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsRzNUTFNFQ0NTSEEzODQy
MDIwQ0ExLTIuY3J0MAwGA1UdEwEB/wQCMAAwggF7BgorBgEEAdZ5AgQCBIIBawSC
AWcBZQB0AA5XlLzzrqk+MxssmQez95Dfm8I9cTIl3SGpJaxhxU4hAAABlGd6v8cA
AAQDAEUwQwIfJBcPWkx80ik7uLYW6OGvNYvJ4NmOR2RXc9uviFPH6QIgUtuuUenH
IT5UNWJffBBRq31tUGi7ZDTSrrM0f4z1Va4AdQBkEcRspBLsp4kcogIuALyrTygH
1B41J6vq/tUDyX3N8AAAAZRnesAFAAAEAwBGMEQCIHCu6NgHhV1Qvif/G7BHq7ci
MGH8jdch/xy4LzrYlesXAiByMFMvDhGg4sYm1MsrDGVedcwpE4eN0RuZcFGmWxwJ
cgB2AEmcm2neHXzs/DbezYdkprhbrwqHgBnRVVL76esp3fjDAAABlGd6wBkAAAQD
AEcwRQIgaFh67yEQ2lwgm3X16n2iWjEQFII2b2fpONtBVibZVWwCIQD5psqjXDYs
IEb1hyh0S8bBN3O4u2sA9zisKIlYjZg8wjAKBggqhkjOPQQDAwNoADBlAjEA+aaC
RlPbb+VY+u4avPyaG7fvUDJqN8KwlrXD4XptT7QL+D03+BA/FUEo3dD1iz37AjBk
Y3jhsuLAW7pWsDbtX/Qwxp6kNsK4jh1/RjvV/260sxQwM/GM7t0+T0uP2L+Y12U=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- # The intermediate certificate for example.com provided by your CA
MIIDeTCCAv+gAwIBAgIQCwDpLU1tcx/KMFnHyx4YhjAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
Fw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFkxCzAJBgNVBAYTAlVTMRUw
EwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2JhbCBH
MyBUTFMgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA
BHipnHWuiF1jpK1dhtgQSdavklljQyOF9EhlMM1KNJWmDj7ZfAjXVwUoSJ4Lq+vC
05ae7UXSi4rOAUsXQ+Fzz21zSDTcAEYJtVZUyV96xxMH0GwYF2zK28cLJlYujQf1
Z6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFIoj655r1/k3
XfltITl2mqFn3hCoMB8GA1UdIwQYMBaAFLPbSKT5ocXYrjZBzBFjaWIpvEvGMA4G
A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI
KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp
Q2VydEdsb2JhbFJvb3RHMy5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny
bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEczLmNybDA9BgNVHSAE
NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG
BmeBDAECAzAKBggqhkjOPQQDAwNoADBlAjB+Jlhu7ojsDN0VQe56uJmZcNFiZU+g
IJ5HsVvBsmcxHcxyeq8ickBCbmWE/odLDxkCMQDmv9auNIdbP2fHHahv1RJ4teaH
MUSpXca4eMzP79QyWBH/OoUGPB2Eb9P1+dozHKQ=
-----END CERTIFICATE-----
Use the included compose.tls.yaml file when deploying SCIM bridge to create Docker secrets and configure SCIM bridge to use this certificate and private key when terminating TLS traffic:
docker stack config --compose-file ./compose.template.yaml \
--compose-file ./compose.tls.yaml |
docker stack deploy --compose-file - op-scim-bridge