CINXE.COM
Thumbor - Wikitech
<!DOCTYPE html> <html class="client-nojs vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-1 vector-feature-appearance-pinned-clientpref-1 vector-feature-night-mode-disabled skin-theme-clientpref-day vector-toc-available" lang="en" dir="ltr"> <head> <meta charset="UTF-8"> <title>Thumbor - Wikitech</title> <script>(function(){var className="client-js vector-feature-language-in-header-enabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-1 vector-feature-appearance-pinned-clientpref-1 vector-feature-night-mode-disabled skin-theme-clientpref-day vector-toc-available";var cookie=document.cookie.match(/(?:^|; )labswikimwclientpreferences=([^;]+)/);if(cookie){cookie[1].split('%2C').forEach(function(pref){className=className.replace(new RegExp('(^| )'+pref.replace(/-clientpref-\w+$|[^\w-]+/g,'')+'-clientpref-\\w+( |$)'),'$1'+pref+'$2');});}document.documentElement.className=className;}());RLCONF={"wgBreakFrames":false,"wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgDefaultDateFormat": "dmy","wgMonthNames":["","January","February","March","April","May","June","July","August","September","October","November","December"],"wgRequestId":"2b288b8b-5b9f-448f-ac44-f818a12296dc","wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Thumbor","wgTitle":"Thumbor","wgCurRevisionId":2144228,"wgRevisionId":2144228,"wgArticleId":35071,"wgIsArticle":true,"wgIsRedirect":false,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":[],"wgPageViewLanguage":"en","wgPageContentLanguage":"en","wgPageContentModel":"wikitext","wgRelevantPageName":"Thumbor","wgRelevantArticleId":35071,"wgIsProbablyEditable":false,"wgRelevantPageIsProbablyEditable":false,"wgRestrictionEdit":[],"wgRestrictionMove":[],"wgNoticeProject":"wikitech","wgCiteReferencePreviewsActive":true,"wgMediaViewerOnClick":true,"wgMediaViewerEnabledByDefault":true,"wgVisualEditor":{"pageLanguageCode":"en","pageLanguageDir":"ltr","pageVariantFallbacks":"en"}, "wgMFDisplayWikibaseDescriptions":{"search":true,"watchlist":true,"tagline":false,"nearby":true},"wgWMESchemaEditAttemptStepOversample":false,"wgWMEPageLength":20000,"wgCentralAuthMobileDomain":false,"wgEditSubmitButtonLabelPublish":true,"wgDiscussionToolsFeaturesEnabled":{"replytool":true,"newtopictool":true,"sourcemodetoolbar":true,"topicsubscription":false,"autotopicsub":false,"visualenhancements":false,"visualenhancements_reply":false,"visualenhancements_pageframe":false},"wgDiscussionToolsFallbackEditMode":"visual","wgULSPosition":"personal","wgULSisCompactLinksEnabled":false,"wgVector2022LanguageInHeader":true,"wgULSisLanguageSelectorEmpty":false,"wgCheckUserClientHintsHeadersJsApi":["brands","architecture","bitness","fullVersionList","mobile","model","platform","platformVersion"],"wgSiteNoticeId":"2.0"};RLSTATE={"ext.globalCssJs.user.styles":"ready","site.styles":"ready","user.styles":"ready","ext.globalCssJs.user":"ready","user":"ready","user.options":"loading", "ext.discussionTools.init.styles":"ready","oojs-ui-core.styles":"ready","oojs-ui.styles.indicators":"ready","mediawiki.widgets.styles":"ready","oojs-ui-core.icons":"ready","skins.vector.search.codex.styles":"ready","skins.vector.styles":"ready","skins.vector.icons":"ready","ext.wikimediamessages.styles":"ready","ext.visualEditor.desktopArticleTarget.noscript":"ready","ext.uls.pt":"ready","ext.dismissableSiteNotice.styles":"ready"};RLPAGEMODULES=["site","mediawiki.page.ready","mediawiki.toc","skins.vector.js","ext.centralNotice.geoIP","ext.centralNotice.startUp","ext.gadget.site","ext.urlShortener.toolbar","ext.centralauth.centralautologin","ext.visualEditor.desktopArticleTarget.init","ext.visualEditor.targetLoader","ext.echo.centralauth","ext.discussionTools.init","ext.eventLogging","ext.wikimediaEvents","ext.uls.interface","ext.checkUser.clientHints","ext.dismissableSiteNotice"];</script> <script>(RLQ=window.RLQ||[]).push(function(){mw.loader.impl(function(){return["user.options@12s5i",function($,jQuery,require,module){mw.user.tokens.set({"patrolToken":"+\\","watchToken":"+\\","csrfToken":"+\\"}); }];});});</script> <link rel="stylesheet" href="/w/load.php?lang=en&modules=ext.discussionTools.init.styles%7Cext.dismissableSiteNotice.styles%7Cext.uls.pt%7Cext.visualEditor.desktopArticleTarget.noscript%7Cext.wikimediamessages.styles%7Cmediawiki.widgets.styles%7Coojs-ui-core.icons%2Cstyles%7Coojs-ui.styles.indicators%7Cskins.vector.icons%2Cstyles%7Cskins.vector.search.codex.styles&only=styles&skin=vector-2022"> <script async="" src="/w/load.php?lang=en&modules=startup&only=scripts&raw=1&skin=vector-2022"></script> <meta name="ResourceLoaderDynamicStyles" content=""> <link rel="stylesheet" href="/w/load.php?lang=en&modules=site.styles&only=styles&skin=vector-2022"> <meta name="generator" content="MediaWiki 1.44.0-wmf.5"> <meta name="referrer" content="origin"> <meta name="referrer" content="origin-when-cross-origin"> <meta name="robots" content="max-image-preview:standard"> <meta name="format-detection" content="telephone=no"> <meta name="viewport" content="width=1120"> <meta property="og:title" content="Thumbor - Wikitech"> <meta property="og:type" content="website"> <link rel="icon" href="/static/favicon/wikitech.ico"> <link rel="search" type="application/opensearchdescription+xml" href="/w/rest.php/v1/search" title="Wikitech (en)"> <link rel="EditURI" type="application/rsd+xml" href="//wikitech.wikimedia.org/w/api.php?action=rsd"> <link rel="canonical" href="https://wikitech.wikimedia.org/wiki/Thumbor"> <link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"> <link rel="alternate" type="application/atom+xml" title="Wikitech Atom feed" href="/w/index.php?title=Special:RecentChanges&feed=atom"> <link rel="dns-prefetch" href="//meta.wikimedia.org" /> <link rel="dns-prefetch" href="//login.wikimedia.org"> </head> <body class="ext-discussiontools-replytool-enabled ext-discussiontools-newtopictool-enabled ext-discussiontools-sourcemodetoolbar-enabled skin--responsive skin-vector skin-vector-search-vue mediawiki ltr sitedir-ltr mw-hide-empty-elt ns-0 ns-subject page-Thumbor rootpage-Thumbor skin-vector-2022 action-view"><a class="mw-jump-link" href="#bodyContent">Jump to content</a> <div class="vector-header-container"> <header class="vector-header mw-header"> <div class="vector-header-start"> <nav class="vector-main-menu-landmark" aria-label="Site"> <div id="vector-main-menu-dropdown" class="vector-dropdown vector-main-menu-dropdown vector-button-flush-left vector-button-flush-right" > <input type="checkbox" id="vector-main-menu-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-main-menu-dropdown" class="vector-dropdown-checkbox " aria-label="Main menu" > <label id="vector-main-menu-dropdown-label" for="vector-main-menu-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-menu mw-ui-icon-wikimedia-menu"></span> <span class="vector-dropdown-label-text">Main menu</span> </label> <div class="vector-dropdown-content"> <div id="vector-main-menu-unpinned-container" class="vector-unpinned-container"> <div id="vector-main-menu" class="vector-main-menu vector-pinnable-element"> <div class="vector-pinnable-header vector-main-menu-pinnable-header vector-pinnable-header-unpinned" data-feature-name="main-menu-pinned" data-pinnable-element-id="vector-main-menu" data-pinned-container-id="vector-main-menu-pinned-container" data-unpinned-container-id="vector-main-menu-unpinned-container" > <div class="vector-pinnable-header-label">Main menu</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-main-menu.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-main-menu.unpin">hide</button> </div> <div id="p-navigation" class="vector-menu mw-portlet mw-portlet-navigation" > <div class="vector-menu-heading"> Navigation </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-mainpage-description" class="mw-list-item"><a href="/wiki/Main_Page" title="Visit the main page [z]" accesskey="z"><span>Main page</span></a></li><li id="n-recentchanges" class="mw-list-item"><a href="/wiki/Special:RecentChanges" title="A list of recent changes in the wiki [r]" accesskey="r"><span>Recent changes</span></a></li><li id="n-Server-admin-log:-Prod" class="mw-list-item"><a href="/wiki/Server_Admin_Log"><span>Server admin log: Prod</span></a></li><li id="n-Admin-log:-RelEng" class="mw-list-item"><a href="/wiki/Release_Engineering/SAL"><span>Admin log: RelEng</span></a></li><li id="n-Incident-status" class="mw-list-item"><a href="/wiki/Incident_status"><span>Incident status</span></a></li><li id="n-Deployments" class="mw-list-item"><a href="/wiki/Deployments"><span>Deployments</span></a></li><li id="n-SRE-Team-Help" class="mw-list-item"><a href="/wiki/SRE/SRE_Team_requests"><span>SRE Team Help</span></a></li> </ul> </div> </div> <div id="p-Cloud_VPS_&_Toolforge" class="vector-menu mw-portlet mw-portlet-Cloud_VPS_Toolforge" > <div class="vector-menu-heading"> Cloud VPS & Toolforge </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-Cloud-VPS-portal" class="mw-list-item"><a href="/wiki/Portal:Cloud_VPS"><span>Cloud VPS portal</span></a></li><li id="n-Toolforge-portal" class="mw-list-item"><a href="/wiki/Portal:Toolforge"><span>Toolforge portal</span></a></li><li id="n-Request-VPS-project" class="mw-list-item"><a href="https://phabricator.wikimedia.org/project/view/2875/"><span>Request VPS project</span></a></li><li id="n-Admin-log:-Cloud-VPS" class="mw-list-item"><a href="/wiki/Cloud_VPS_Server_Admin_Log"><span>Admin log: Cloud VPS</span></a></li> </ul> </div> </div> </div> </div> </div> </div> </nav> <a href="/wiki/Main_Page" class="mw-logo"> <img class="mw-logo-icon" src="/static/images/icons/wikitech.svg" alt="" aria-hidden="true" height="50" width="50"> <span class="mw-logo-container skin-invert"> <img class="mw-logo-wordmark" alt="Wikitech" src="/static/images/mobile/copyright/wikitech-wordmark.svg" style="width: 8.75em; height: 1.6875em;"> </span> </a> </div> <div class="vector-header-end"> <div id="p-search" role="search" class="vector-search-box-vue vector-search-box-collapses vector-search-box-show-thumbnail vector-search-box-auto-expand-width vector-search-box"> <a href="/wiki/Special:Search" class="cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only search-toggle" title="Search Wikitech [f]" accesskey="f"><span class="vector-icon mw-ui-icon-search mw-ui-icon-wikimedia-search"></span> <span>Search</span> </a> <div class="vector-typeahead-search-container"> <div class="cdx-typeahead-search cdx-typeahead-search--show-thumbnail cdx-typeahead-search--auto-expand-width"> <form action="/w/index.php" id="searchform" class="cdx-search-input cdx-search-input--has-end-button"> <div id="simpleSearch" class="cdx-search-input__input-wrapper" data-search-loc="header-moved"> <div class="cdx-text-input cdx-text-input--has-start-icon"> <input class="cdx-text-input__input" type="search" name="search" placeholder="Search Wikitech" aria-label="Search Wikitech" autocapitalize="sentences" title="Search Wikitech [f]" accesskey="f" id="searchInput" > <span class="cdx-text-input__icon cdx-text-input__start-icon"></span> </div> <input type="hidden" name="title" value="Special:Search"> </div> <button class="cdx-button cdx-search-input__end-button">Search</button> </form> </div> </div> </div> <nav class="vector-user-links vector-user-links-wide" aria-label="Personal tools"> <div class="vector-user-links-main"> <div id="p-vector-user-menu-preferences" class="vector-menu mw-portlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-uls" class="mw-list-item active user-links-collapsible-item"><a data-mw="interface" href="#" class="uls-trigger cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet"><span class="vector-icon mw-ui-icon-wikimedia-language mw-ui-icon-wikimedia-wikimedia-language"></span> <span>English</span></a> </li> </ul> </div> </div> <div id="p-vector-user-menu-userpage" class="vector-menu mw-portlet emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> <nav class="vector-appearance-landmark" aria-label="Appearance"> <div id="vector-appearance-dropdown" class="vector-dropdown " title="Change the appearance of the page's font size, width, and color" > <input type="checkbox" id="vector-appearance-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-appearance-dropdown" class="vector-dropdown-checkbox " aria-label="Appearance" > <label id="vector-appearance-dropdown-label" for="vector-appearance-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-appearance mw-ui-icon-wikimedia-appearance"></span> <span class="vector-dropdown-label-text">Appearance</span> </label> <div class="vector-dropdown-content"> <div id="vector-appearance-unpinned-container" class="vector-unpinned-container"> </div> </div> </div> </nav> <div id="p-vector-user-menu-notifications" class="vector-menu mw-portlet emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> <div id="p-vector-user-menu-overflow" class="vector-menu mw-portlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="pt-sitesupport-2" class="user-links-collapsible-item mw-list-item user-links-collapsible-item"><a data-mw="interface" href="https://donate.wikimedia.org/?wmf_source=donate&wmf_medium=sidebar&wmf_campaign=wikitech.wikimedia.org&uselang=en" class=""><span>Donate</span></a> </li> <li id="pt-login-2" class="user-links-collapsible-item mw-list-item user-links-collapsible-item"><a data-mw="interface" href="/w/index.php?title=Special:UserLogin&returnto=Thumbor" title="You are encouraged to log in; however, it is not mandatory [o]" accesskey="o" class=""><span>Log in</span></a> </li> </ul> </div> </div> </div> <div id="vector-user-links-dropdown" class="vector-dropdown vector-user-menu vector-button-flush-right vector-user-menu-logged-out user-links-collapsible-item" title="More options" > <input type="checkbox" id="vector-user-links-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-user-links-dropdown" class="vector-dropdown-checkbox " aria-label="Personal tools" > <label id="vector-user-links-dropdown-label" for="vector-user-links-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-ellipsis mw-ui-icon-wikimedia-ellipsis"></span> <span class="vector-dropdown-label-text">Personal tools</span> </label> <div class="vector-dropdown-content"> <div id="p-personal" class="vector-menu mw-portlet mw-portlet-personal user-links-collapsible-item" title="User menu" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="pt-sitesupport" class="user-links-collapsible-item mw-list-item"><a href="https://donate.wikimedia.org/?wmf_source=donate&wmf_medium=sidebar&wmf_campaign=wikitech.wikimedia.org&uselang=en"><span>Donate</span></a></li><li id="pt-login" class="user-links-collapsible-item mw-list-item"><a href="/w/index.php?title=Special:UserLogin&returnto=Thumbor" title="You are encouraged to log in; however, it is not mandatory [o]" accesskey="o"><span class="vector-icon mw-ui-icon-logIn mw-ui-icon-wikimedia-logIn"></span> <span>Log in</span></a></li> </ul> </div> </div> </div> </div> </nav> </div> </header> </div> <div class="mw-page-container"> <div class="mw-page-container-inner"> <div class="vector-sitenotice-container"> <div id="siteNotice"><div id="mw-dismissablenotice-anonplace"></div><script>(function(){var node=document.getElementById("mw-dismissablenotice-anonplace");if(node){node.outerHTML="\u003Cdiv class=\"mw-dismissable-notice\"\u003E\u003Cdiv class=\"mw-dismissable-notice-close\"\u003E[\u003Ca tabindex=\"0\" role=\"button\"\u003Edismiss\u003C/a\u003E]\u003C/div\u003E\u003Cdiv class=\"mw-dismissable-notice-body\"\u003E\u003C!-- CentralNotice --\u003E\u003Cdiv id=\"localNotice\" data-nosnippet=\"\"\u003E\u003Cdiv class=\"sitenotice\" lang=\"en\" dir=\"ltr\"\u003E\u003Ctable style=\"width: 75%; background-color: var(--background-color-warning-subtle, #fdf2d5); border: var(--border-subtle, 1px solid #987027); color: var(--color-base, #202122); border-radius: 10px; padding: 5px; margin: 0 auto;\"\u003E\n\u003Ctbody\u003E\u003Ctr\u003E\n\u003Ctd style=\"width:40px; height:40px; text-align:center; vertical-align:middle; padding: 2px;\"\u003E\u003Cspan typeof=\"mw:File\"\u003E\u003Ca href=\"/wiki/File:OOjs_UI_icon_alert-warning.svg\" class=\"mw-file-description\"\u003E\u003Cimg src=\"//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/OOjs_UI_icon_alert-warning.svg/30px-OOjs_UI_icon_alert-warning.svg.png\" decoding=\"async\" width=\"30\" height=\"30\" class=\"mw-file-element\" srcset=\"//upload.wikimedia.org/wikipedia/commons/thumb/3/3b/OOjs_UI_icon_alert-warning.svg/45px-OOjs_UI_icon_alert-warning.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/3/3b/OOjs_UI_icon_alert-warning.svg/60px-OOjs_UI_icon_alert-warning.svg.png 2x\" data-file-width=\"20\" data-file-height=\"20\" /\u003E\u003C/a\u003E\u003C/span\u003E\n\u003C/td\u003E\n\u003Ctd style=\"text-align:center; vertical-align: middle; padding: 4px; max-height: 60px;\"\u003E\u003Cb\u003EWe are migrating Wikitech to \u003Ca href=\"/wiki/Wikitech/SUL-migration\" title=\"Wikitech/SUL-migration\"\u003ESUL\u003C/a\u003E!\u003C/b\u003E\n\u003Cp\u003E\u003Cb\u003EAction may be required for your \u003Ca href=\"/wiki/Wikitech/SUL-migration#What_You_Should_Do\" title=\"Wikitech/SUL-migration\"\u003E account\u003C/a\u003E!\u003C/b\u003E\n\u003C/p\u003E\u003Cp\u003E\u003Cb\u003ETrouble logging in? Please visit \u003Ca href=\"https://phabricator.wikimedia.org/T376267\" class=\"extiw\" title=\"phab:T376267\"\u003ET376267\u003C/a\u003E\u003C/b\u003E\n\u003C/p\u003E\n\u003C/td\u003E\u003C/tr\u003E\u003C/tbody\u003E\u003C/table\u003E\u003C/div\u003E\u003C/div\u003E\u003C/div\u003E\u003C/div\u003E";}}());</script></div> </div> <div class="vector-column-start"> <div class="vector-main-menu-container"> <div id="mw-navigation"> <nav id="mw-panel" class="vector-main-menu-landmark" aria-label="Site"> <div id="vector-main-menu-pinned-container" class="vector-pinned-container"> </div> </nav> </div> </div> <div class="vector-sticky-pinned-container"> <nav id="mw-panel-toc" aria-label="Contents" data-event-name="ui.sidebar-toc" class="mw-table-of-contents-container vector-toc-landmark"> <div id="vector-toc-pinned-container" class="vector-pinned-container"> <div id="vector-toc" class="vector-toc vector-pinnable-element"> <div class="vector-pinnable-header vector-toc-pinnable-header vector-pinnable-header-pinned" data-feature-name="toc-pinned" data-pinnable-element-id="vector-toc" > <h2 class="vector-pinnable-header-label">Contents</h2> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-toc.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-toc.unpin">hide</button> </div> <ul class="vector-toc-contents" id="mw-panel-toc-list"> <li id="toc-mw-content-text" class="vector-toc-list-item vector-toc-level-1"> <a href="#" class="vector-toc-link"> <div class="vector-toc-text">Beginning</div> </a> </li> <li id="toc-Rationale" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Rationale"> <div class="vector-toc-text"> <span class="vector-toc-numb">1</span> <span>Rationale</span> </div> </a> <ul id="toc-Rationale-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Supported_file_types" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Supported_file_types"> <div class="vector-toc-text"> <span class="vector-toc-numb">2</span> <span>Supported file types</span> </div> </a> <ul id="toc-Supported_file_types-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Broader_ecosystem" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Broader_ecosystem"> <div class="vector-toc-text"> <span class="vector-toc-numb">3</span> <span>Broader ecosystem</span> </div> </a> <button aria-controls="toc-Broader_ecosystem-sublist" class="cdx-button cdx-button--weight-quiet cdx-button--icon-only vector-toc-toggle"> <span class="vector-icon mw-ui-icon-wikimedia-expand"></span> <span>Toggle Broader ecosystem subsection</span> </button> <ul id="toc-Broader_ecosystem-sublist" class="vector-toc-list"> <li id="toc-Public_wikis" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Public_wikis"> <div class="vector-toc-text"> <span class="vector-toc-numb">3.1</span> <span>Public wikis</span> </div> </a> <ul id="toc-Public_wikis-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Private_wikis" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Private_wikis"> <div class="vector-toc-text"> <span class="vector-toc-numb">3.2</span> <span>Private wikis</span> </div> </a> <ul id="toc-Private_wikis-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Hitting_Thumbor_(common)" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Hitting_Thumbor_(common)"> <div class="vector-toc-text"> <span class="vector-toc-numb">3.3</span> <span>Hitting Thumbor (common)</span> </div> </a> <ul id="toc-Hitting_Thumbor_(common)-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> <li id="toc-Ways_our_use_of_Thumbor_deviates_from_its_original_intent" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Ways_our_use_of_Thumbor_deviates_from_its_original_intent"> <div class="vector-toc-text"> <span class="vector-toc-numb">4</span> <span>Ways our use of Thumbor deviates from its original intent</span> </div> </a> <button aria-controls="toc-Ways_our_use_of_Thumbor_deviates_from_its_original_intent-sublist" class="cdx-button cdx-button--weight-quiet cdx-button--icon-only vector-toc-toggle"> <span class="vector-icon mw-ui-icon-wikimedia-expand"></span> <span>Toggle Ways our use of Thumbor deviates from its original intent subsection</span> </button> <ul id="toc-Ways_our_use_of_Thumbor_deviates_from_its_original_intent-sublist" class="vector-toc-list"> <li id="toc-Disk_access" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Disk_access"> <div class="vector-toc-text"> <span class="vector-toc-numb">4.1</span> <span>Disk access</span> </div> </a> <ul id="toc-Disk_access-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Filters" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Filters"> <div class="vector-toc-text"> <span class="vector-toc-numb">4.2</span> <span>Filters</span> </div> </a> <ul id="toc-Filters-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Image_processing_ordering" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Image_processing_ordering"> <div class="vector-toc-text"> <span class="vector-toc-numb">4.3</span> <span>Image processing ordering</span> </div> </a> <ul id="toc-Image_processing_ordering-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Subprocesses" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Subprocesses"> <div class="vector-toc-text"> <span class="vector-toc-numb">4.4</span> <span>Subprocesses</span> </div> </a> <ul id="toc-Subprocesses-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Multi-engine_setup" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Multi-engine_setup"> <div class="vector-toc-text"> <span class="vector-toc-numb">4.5</span> <span>Multi-engine setup</span> </div> </a> <ul id="toc-Multi-engine_setup-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> <li id="toc-Throttling" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Throttling"> <div class="vector-toc-text"> <span class="vector-toc-numb">5</span> <span>Throttling</span> </div> </a> <button aria-controls="toc-Throttling-sublist" class="cdx-button cdx-button--weight-quiet cdx-button--icon-only vector-toc-toggle"> <span class="vector-icon mw-ui-icon-wikimedia-expand"></span> <span>Toggle Throttling subsection</span> </button> <ul id="toc-Throttling-sublist" class="vector-toc-list"> <li id="toc-Memcached-based" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Memcached-based"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.1</span> <span>Memcached-based</span> </div> </a> <ul id="toc-Memcached-based-sublist" class="vector-toc-list"> <li id="toc-Failure" class="vector-toc-list-item vector-toc-level-3"> <a class="vector-toc-link" href="#Failure"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.1.1</span> <span>Failure</span> </div> </a> <ul id="toc-Failure-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> <li id="toc-Poolcounter-based" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Poolcounter-based"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.2</span> <span>Poolcounter-based</span> </div> </a> <ul id="toc-Poolcounter-based-sublist" class="vector-toc-list"> <li id="toc-Per-IP" class="vector-toc-list-item vector-toc-level-3"> <a class="vector-toc-link" href="#Per-IP"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.2.1</span> <span>Per-IP</span> </div> </a> <ul id="toc-Per-IP-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Per-original" class="vector-toc-list-item vector-toc-level-3"> <a class="vector-toc-link" href="#Per-original"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.2.2</span> <span>Per-original</span> </div> </a> <ul id="toc-Per-original-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Expensive" class="vector-toc-list-item vector-toc-level-3"> <a class="vector-toc-link" href="#Expensive"> <div class="vector-toc-text"> <span class="vector-toc-numb">5.2.3</span> <span>Expensive</span> </div> </a> <ul id="toc-Expensive-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> </ul> </li> <li id="toc-Logging" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Logging"> <div class="vector-toc-text"> <span class="vector-toc-numb">6</span> <span>Logging</span> </div> </a> <ul id="toc-Logging-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Configuration" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Configuration"> <div class="vector-toc-text"> <span class="vector-toc-numb">7</span> <span>Configuration</span> </div> </a> <button aria-controls="toc-Configuration-sublist" class="cdx-button cdx-button--weight-quiet cdx-button--icon-only vector-toc-toggle"> <span class="vector-icon mw-ui-icon-wikimedia-expand"></span> <span>Toggle Configuration subsection</span> </button> <ul id="toc-Configuration-sublist" class="vector-toc-list"> <li id="toc-Scaling" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Scaling"> <div class="vector-toc-text"> <span class="vector-toc-numb">7.1</span> <span>Scaling</span> </div> </a> <ul id="toc-Scaling-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> <li id="toc-Updating_custom_plugins" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Updating_custom_plugins"> <div class="vector-toc-text"> <span class="vector-toc-numb">8</span> <span>Updating custom plugins</span> </div> </a> <button aria-controls="toc-Updating_custom_plugins-sublist" class="cdx-button cdx-button--weight-quiet cdx-button--icon-only vector-toc-toggle"> <span class="vector-icon mw-ui-icon-wikimedia-expand"></span> <span>Toggle Updating custom plugins subsection</span> </button> <ul id="toc-Updating_custom_plugins-sublist" class="vector-toc-list"> <li id="toc-Testing_the_changes" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Testing_the_changes"> <div class="vector-toc-text"> <span class="vector-toc-numb">8.1</span> <span>Testing the changes</span> </div> </a> <ul id="toc-Testing_the_changes-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Deployment" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Deployment"> <div class="vector-toc-text"> <span class="vector-toc-numb">8.2</span> <span>Deployment</span> </div> </a> <ul id="toc-Deployment-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Restarting" class="vector-toc-list-item vector-toc-level-2"> <a class="vector-toc-link" href="#Restarting"> <div class="vector-toc-text"> <span class="vector-toc-numb">8.3</span> <span>Restarting</span> </div> </a> <ul id="toc-Restarting-sublist" class="vector-toc-list"> </ul> </li> </ul> </li> <li id="toc-Dashboards_and_logs" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Dashboards_and_logs"> <div class="vector-toc-text"> <span class="vector-toc-numb">9</span> <span>Dashboards and logs</span> </div> </a> <ul id="toc-Dashboards_and_logs-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Manhole" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Manhole"> <div class="vector-toc-text"> <span class="vector-toc-numb">10</span> <span>Manhole</span> </div> </a> <ul id="toc-Manhole-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Local_development" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#Local_development"> <div class="vector-toc-text"> <span class="vector-toc-numb">11</span> <span>Local development</span> </div> </a> <ul id="toc-Local_development-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-See_also" class="vector-toc-list-item vector-toc-level-1"> <a class="vector-toc-link" href="#See_also"> <div class="vector-toc-text"> <span class="vector-toc-numb">12</span> <span>See also</span> </div> </a> <ul id="toc-See_also-sublist" class="vector-toc-list"> </ul> </li> </ul> </div> </div> </nav> </div> </div> <div class="mw-content-container"> <main id="content" class="mw-body"> <header class="mw-body-header vector-page-titlebar"> <nav aria-label="Contents" class="vector-toc-landmark"> <div id="vector-page-titlebar-toc" class="vector-dropdown vector-page-titlebar-toc vector-button-flush-left" > <input type="checkbox" id="vector-page-titlebar-toc-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-page-titlebar-toc" class="vector-dropdown-checkbox " aria-label="Toggle the table of contents" > <label id="vector-page-titlebar-toc-label" for="vector-page-titlebar-toc-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-listBullet mw-ui-icon-wikimedia-listBullet"></span> <span class="vector-dropdown-label-text">Toggle the table of contents</span> </label> <div class="vector-dropdown-content"> <div id="vector-page-titlebar-toc-unpinned-container" class="vector-unpinned-container"> </div> </div> </div> </nav> <h1 id="firstHeading" class="firstHeading mw-first-heading"><span class="mw-page-title-main">Thumbor</span></h1> </header> <div class="vector-page-toolbar"> <div class="vector-page-toolbar-container"> <div id="left-navigation"> <nav aria-label="Namespaces"> <div id="p-associated-pages" class="vector-menu vector-menu-tabs mw-portlet mw-portlet-associated-pages" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-nstab-main" class="selected vector-tab-noicon mw-list-item"><a href="/wiki/Thumbor" title="View the content page [c]" accesskey="c"><span>Page</span></a></li><li id="ca-talk" class="new vector-tab-noicon mw-list-item"><a href="/w/index.php?title=Talk:Thumbor&action=edit&redlink=1" rel="discussion" class="new" title="Discussion about the content page (page does not exist) [t]" accesskey="t"><span>Discussion</span></a></li> </ul> </div> </div> <div id="vector-variants-dropdown" class="vector-dropdown emptyPortlet" > <input type="checkbox" id="vector-variants-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-variants-dropdown" class="vector-dropdown-checkbox " aria-label="Change language variant" > <label id="vector-variants-dropdown-label" for="vector-variants-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet" aria-hidden="true" ><span class="vector-dropdown-label-text">English</span> </label> <div class="vector-dropdown-content"> <div id="p-variants" class="vector-menu mw-portlet mw-portlet-variants emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> </div> </div> </nav> </div> <div id="right-navigation" class="vector-collapsible"> <nav aria-label="Views"> <div id="p-views" class="vector-menu vector-menu-tabs mw-portlet mw-portlet-views" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-view" class="selected vector-tab-noicon mw-list-item"><a href="/wiki/Thumbor"><span>Read</span></a></li><li id="ca-viewsource" class="vector-tab-noicon mw-list-item"><a href="/w/index.php?title=Thumbor&action=edit" title="This page is protected. You can view its source [e]" accesskey="e"><span>View source</span></a></li><li id="ca-history" class="vector-tab-noicon mw-list-item"><a href="/w/index.php?title=Thumbor&action=history" title="Past revisions of this page [h]" accesskey="h"><span>View history</span></a></li> </ul> </div> </div> </nav> <nav class="vector-page-tools-landmark" aria-label="Page tools"> <div id="vector-page-tools-dropdown" class="vector-dropdown vector-page-tools-dropdown" > <input type="checkbox" id="vector-page-tools-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-page-tools-dropdown" class="vector-dropdown-checkbox " aria-label="Tools" > <label id="vector-page-tools-dropdown-label" for="vector-page-tools-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet" aria-hidden="true" ><span class="vector-dropdown-label-text">Tools</span> </label> <div class="vector-dropdown-content"> <div id="vector-page-tools-unpinned-container" class="vector-unpinned-container"> <div id="vector-page-tools" class="vector-page-tools vector-pinnable-element"> <div class="vector-pinnable-header vector-page-tools-pinnable-header vector-pinnable-header-unpinned" data-feature-name="page-tools-pinned" data-pinnable-element-id="vector-page-tools" data-pinned-container-id="vector-page-tools-pinned-container" data-unpinned-container-id="vector-page-tools-unpinned-container" > <div class="vector-pinnable-header-label">Tools</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-page-tools.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-page-tools.unpin">hide</button> </div> <div id="p-cactions" class="vector-menu mw-portlet mw-portlet-cactions emptyPortlet vector-has-collapsible-items" title="More options" > <div class="vector-menu-heading"> Actions </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-more-view" class="selected vector-more-collapsible-item mw-list-item"><a href="/wiki/Thumbor"><span>Read</span></a></li><li id="ca-more-viewsource" class="vector-more-collapsible-item mw-list-item"><a href="/w/index.php?title=Thumbor&action=edit"><span>View source</span></a></li><li id="ca-more-history" class="vector-more-collapsible-item mw-list-item"><a href="/w/index.php?title=Thumbor&action=history"><span>View history</span></a></li> </ul> </div> </div> <div id="p-tb" class="vector-menu mw-portlet mw-portlet-tb" > <div class="vector-menu-heading"> General </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="t-whatlinkshere" class="mw-list-item"><a href="/wiki/Special:WhatLinksHere/Thumbor" title="A list of all wiki pages that link here [j]" accesskey="j"><span>What links here</span></a></li><li id="t-recentchangeslinked" class="mw-list-item"><a href="/wiki/Special:RecentChangesLinked/Thumbor" rel="nofollow" title="Recent changes in pages linked from this page [k]" accesskey="k"><span>Related changes</span></a></li><li id="t-specialpages" class="mw-list-item"><a href="/wiki/Special:SpecialPages" title="A list of all special pages [q]" accesskey="q"><span>Special pages</span></a></li><li id="t-permalink" class="mw-list-item"><a href="/w/index.php?title=Thumbor&oldid=2144228" title="Permanent link to this revision of this page"><span>Permanent link</span></a></li><li id="t-info" class="mw-list-item"><a href="/w/index.php?title=Thumbor&action=info" title="More information about this page"><span>Page information</span></a></li><li id="t-cite" class="mw-list-item"><a href="/w/index.php?title=Special:CiteThisPage&page=Thumbor&id=2144228&wpFormIdentifier=titleform" title="Information on how to cite this page"><span>Cite this page</span></a></li><li id="t-urlshortener" class="mw-list-item"><a href="/w/index.php?title=Special:UrlShortener&url=https%3A%2F%2Fwikitech.wikimedia.org%2Fwiki%2FThumbor"><span>Get shortened URL</span></a></li><li id="t-urlshortener-qrcode" class="mw-list-item"><a href="/w/index.php?title=Special:QrCode&url=https%3A%2F%2Fwikitech.wikimedia.org%2Fwiki%2FThumbor"><span>Download QR code</span></a></li> </ul> </div> </div> <div id="p-coll-print_export" class="vector-menu mw-portlet mw-portlet-coll-print_export" > <div class="vector-menu-heading"> Print/export </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="coll-create_a_book" class="mw-list-item"><a href="/w/index.php?title=Special:Book&bookcmd=book_creator&referer=Thumbor"><span>Create a book</span></a></li><li id="coll-download-as-rl" class="mw-list-item"><a href="/w/index.php?title=Special:DownloadAsPdf&page=Thumbor&action=show-download-screen"><span>Download as PDF</span></a></li><li id="t-print" class="mw-list-item"><a href="/w/index.php?title=Thumbor&printable=yes" title="Printable version of this page [p]" accesskey="p"><span>Printable version</span></a></li> </ul> </div> </div> </div> </div> </div> </div> </nav> </div> </div> </div> <div class="vector-column-end"> <div class="vector-sticky-pinned-container"> <nav class="vector-page-tools-landmark" aria-label="Page tools"> <div id="vector-page-tools-pinned-container" class="vector-pinned-container"> </div> </nav> <nav class="vector-appearance-landmark" aria-label="Appearance"> <div id="vector-appearance-pinned-container" class="vector-pinned-container"> <div id="vector-appearance" class="vector-appearance vector-pinnable-element"> <div class="vector-pinnable-header vector-appearance-pinnable-header vector-pinnable-header-pinned" data-feature-name="appearance-pinned" data-pinnable-element-id="vector-appearance" data-pinned-container-id="vector-appearance-pinned-container" data-unpinned-container-id="vector-appearance-unpinned-container" > <div class="vector-pinnable-header-label">Appearance</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-appearance.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-appearance.unpin">hide</button> </div> </div> </div> </nav> </div> </div> <div id="bodyContent" class="vector-body" aria-labelledby="firstHeading" data-mw-ve-target-container> <div class="vector-body-before-content"> <div class="mw-indicators"> </div> <div id="siteSub" class="noprint">From Wikitech</div> </div> <div id="contentSub"><div id="mw-content-subtitle"></div></div> <div id="mw-content-text" class="mw-body-content"><div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr"><style data-mw-deduplicate="TemplateStyles:r2230541">.mw-parser-output .tpl-navinfra--portal{float:none}.mw-parser-output .tpl-navinfra--portal .tpl-navsidebar-contents{margin:0 8px;display:flex;flex-flow:row wrap;gap:16px}.mw-parser-output .tpl-navinfra--portal .tpl-navsidebar-content{break-inside:avoid}.mw-parser-output .tpl-navinfra--sidebar .tpl-navsidebar-content{margin:0}.mw-parser-output .tpl-navinfra--sidebar .tpl-navsidebar-heading{font-weight:normal;margin:1px 0 0 0}.mw-parser-output .tpl-navinfra--sidebar .tpl-navsidebar-heading a{display:block;border-top:1px solid var(--border-color-muted,#eaecf0);background-color:var(--background-color-neutral-subtle,#f8f9fa);color:var(--color-base,#202122);margin:0 -8px;padding:4px 8px 0px 8px}.mw-parser-output .tpl-navinfra--sidebar .tpl-navsidebar-heading a:hover{background-color:var(--background-color-base,#fff);color:var(--color-emphasized,#000)}.mw-parser-output ul{margin-top:unset!important}</style> <style data-mw-deduplicate="TemplateStyles:r2241375">.mw-parser-output .tpl-navsidebar{max-width:22em;background:var(--background-color-base,#fff);color:var(--color-base,#202122);border:1px solid var(--border-color-base,#a2a9b1);float:right;clear:right;margin:.5em 0 1em 1em}.mw-parser-output .tpl-navsidebar-floatright{float:right;clear:right;margin:.5em 0 1em 1em}.mw-parser-output .tpl-navsidebar-floatleft{float:left;clear:left;margin:.5em 1em 1em 0}.mw-parser-output .tpl-navsidebar-floatnone{float:none;clear:both;margin:.5em 0}.mw-parser-output .tpl-navsidebar-topimage{margin:0 0 16px 0}.mw-parser-output .tpl-navsidebar-title{margin:8px 16px;border-bottom:3px solid var(--border-color-muted,#eaecf0);font-size:20px;text-align:center}.mw-parser-output .tpl-navsidebar-image{margin:0 0 8px}.mw-parser-output .tpl-navsidebar-content{margin:0 0 16px 0;padding:0 8px}.mw-parser-output .tpl-navsidebar-heading{margin:8px 0;font-weight:bold}.mw-parser-output .tpl-navsidebar-foot{padding:0 8px;margin:0;text-align:right;font-size:smaller}@media not (min-width:720px){.mw-parser-output .tpl-navsidebar{float:none;clear:both;margin:.5em 0;max-width:none}}</style><div role="navigation" class="navigation-not-searchable tpl-navsidebar tpl-navinfra--sidebar" style=""><p class="tpl-navsidebar-title"><a href="/wiki/Wikimedia_infrastructure" title="Wikimedia infrastructure">Wikimedia infrastructure</a></p><div class="tpl-navsidebar-contents"><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Data_centers" title="Data centers">Data centers</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Network_design" title="Network design">Networking</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Global_traffic_routing" title="Global traffic routing">Global traffic routing</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/MediaWiki_at_WMF" title="MediaWiki at WMF">MediaWiki SRE</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/MediaWiki_Engineering" title="MediaWiki Engineering">MediaWiki Engineering</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Upload.wikimedia.org" title="Upload.wikimedia.org">Multimedia</a></p><p class="mw-empty-elt"> </p><ul><li><a class="mw-selflink selflink">Thumbor</a></li> <li><a href="/wiki/Swift" title="Swift">Swift</a></li> <li><a href="/wiki/Media_storage" title="Media storage">Media storage</a></li> <li><a href="/wiki/Upload.wikimedia.org" title="Upload.wikimedia.org">upload.wikimedia.org</a></li> <li><a href="/wiki/Media_storage/Backups" title="Media storage/Backups">Media backups</a></li></ul> </div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Data_Engineering" class="mw-redirect" title="Data Engineering">Data Engineering</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Search" title="Search">Search</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/SRE/Data_Persistence/Documentation" title="SRE/Data Persistence/Documentation">SRE Data Persistence</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/SRE/Infrastructure_Foundations/Documentation" title="SRE/Infrastructure Foundations/Documentation">SRE Infra Foundations</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/SRE/Observability/Documentation" title="SRE/Observability/Documentation">SRE Observability</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Performance" title="Performance">Wikimedia Performance</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Event_Platform" title="Event Platform">Event Platform</a></p><p class="mw-empty-elt"> </p></div><div class="tpl-navsidebar-content"> <p class="tpl-navsidebar-heading"><a href="/wiki/Release_Engineering" title="Release Engineering">Release Engineering</a></p><p class="mw-empty-elt"> </p></div></div><p class="tpl-navsidebar-foot">[<span class="noprint plainlinks"><a class="external text" href="https://wikitech.wikimedia.org/w/index.php?title=Template:Navigation_Wikimedia_infrastructure&action=edit"><span title="Edit this template">edit</span></a></span>]</p></div> <dl><dd><i>For common tasks related to Thumbor, see <a href="/wiki/Thumbor/Runbook" title="Thumbor/Runbook">Thumbor/Runbook</a></i></dd></dl> <p>The Wikimedia media thumbnailing infrastructure is based on <a rel="nofollow" class="external text" href="http://thumbor.org/">Thumbor</a>. </p><p>As of June 2017, all thumbnail traffic for public and beta wikis is served and rendered by Thumbor (launch task: <a href="https://phabricator.wikimedia.org/T121388" class="extiw" title="phab:T121388">T121388</a>). </p><p>As of February 2018, all thumbnail traffic for private wikis is served by Thumbor. </p><p>As of April 2018, the MediaWiki imagescaler hardware has been repurposed (<a href="https://phabricator.wikimedia.org/T188062" class="extiw" title="phab:T188062">T188062</a>). </p><p>As of April 2023, Thumbor is serving production traffic from Kubernetes. </p> <meta property="mw:PageProp/toc"/> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Rationale" data-mw-thread-id="h-Rationale"><span data-mw-comment-start="" id="h-Rationale"></span>Rationale<span data-mw-comment-end="h-Rationale"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Rationale","replies":[]}}--></div> <ol><li><b>Better security isolation</b>. Thumbor is stateless and connects to Swift, Poolcounter and DC-local Thumbor-specific Memcache instances (see "Throttling" below). In contrast, MediaWiki is connected to many more services, as well as user data and sessions. Considering how common security vulnerability discoveries are in media-processing software, it makes sense to isolate media thumbnailing as much as possible.</li> <li><b>Better support.</b> Thumbor has a lively community of its own, and is a healthy open-source project. In contrast, the media-handling code in MediaWiki is supported on a best-effort basis by very few people.</li> <li><b>Easier operations.</b> Thumbor is a simple service and should be easy to operate.</li></ol> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Supported_file_types" data-mw-thread-id="h-Supported_file_types"><span data-mw-comment-start="" id="h-Supported_file_types"></span>Supported file types<span data-mw-comment-end="h-Supported_file_types"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Supported_file_types","replies":[]}}--></div> <p>We have written <a href="https://gerrit.wikimedia.org/g/operations/software/thumbor-plugins" class="extiw" title="git:operations/software/thumbor-plugins">Thumbor engines</a> for all the file formats used on Wikimedia wikis. Follow these links for special information about the Thumbor engines for those formats: </p> <ul><li><a href="/wiki/Thumbor/JPEG" title="Thumbor/JPEG">Thumbor/JPEG</a></li> <li><a href="/wiki/Thumbor/PNG" title="Thumbor/PNG">Thumbor/PNG</a></li> <li><a href="/wiki/Thumbor/GIF" title="Thumbor/GIF">Thumbor/GIF</a></li> <li><a href="/wiki/Thumbor/TIFF" title="Thumbor/TIFF">Thumbor/TIFF</a></li> <li><a href="/wiki/Thumbor/XCF" title="Thumbor/XCF">Thumbor/XCF</a></li> <li><a href="/wiki/Thumbor/SVG" title="Thumbor/SVG">Thumbor/SVG</a></li> <li><a href="/wiki/Thumbor/PDF" title="Thumbor/PDF">Thumbor/PDF</a></li> <li><a href="/wiki/Thumbor/DjVu" title="Thumbor/DjVu">Thumbor/DjVu</a></li> <li><a href="/wiki/Thumbor/Video" title="Thumbor/Video">Thumbor/Video</a> (WEBM, OGV)</li> <li><a href="/wiki/Thumbor/STL" title="Thumbor/STL">Thumbor/STL</a></li></ul> <p>These engines are a reimplementation of the logic that resides in MediaWiki core and extensions for the same image formats, often leveraging the same underlying open-source libraries or executables. Whenever possible, reference images generated with MediaWiki are used for the Thumbor integration tests. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Broader_ecosystem" data-mw-thread-id="h-Broader_ecosystem"><span data-mw-comment-start="" id="h-Broader_ecosystem"></span>Broader ecosystem<span data-mw-comment-end="h-Broader_ecosystem"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Broader_ecosystem","replies":["h-Public_wikis-Broader_ecosystem","h-Private_wikis-Broader_ecosystem","h-Hitting_Thumbor_(common)-Broader_ecosystem"]}}--></div> <p>In order to understand Thumbor's role in our software stack, one has to understand how Wikimedia production is currently serving those images. </p> <div class="mw-heading mw-heading3"><h3 id="Public_wikis" data-mw-thread-id="h-Public_wikis-Broader_ecosystem"><span data-mw-comment-start="" id="h-Public_wikis-Broader_ecosystem"></span>Public wikis<span data-mw-comment-end="h-Public_wikis-Broader_ecosystem"></span></h3></div> <p>The edge, where user requests first land, is Varnish. Most requests for a thumbnail are a hit on the Varnish frontend or backend caches. </p><p>When Varnish can't find a copy of the requested thumbnail - whether it's a thumbnail that has never been requested before, or ones that fell out of Varnish cache - Varnish hits the Swift proxies. We run <a href="https://phabricator.wikimedia.org/source/operations-puppet/browse/production/modules/swift/files/python3.9/SwiftMedia/wmf/rewrite.py" class="extiw" title="phab:source/operations-puppet/browse/production/modules/swift/files/python3.9/SwiftMedia/wmf/rewrite.py">a custom plugin on our Swift proxies</a>, which is responsible for parsing the thumbnail URL, determining whether there is a copy of that thumbnail already stored in Swift, serving it if that's the case, asking Thumbor to generate it otherwise. </p><p><i>There is one exception to this workflow, which is when a request is made directly to thumb.php. In that case the request isn't cached by Varnish and is send to MediaWiki, which then proxies it to Thumbor. This is the same behavior used by private wikis, described below. These requests are actually undesirable because of their inefficiency (skipping Varnish caching) and are all coming from gadgets, not from MediaWiki itself. It would be interesting to perform a cleanup campaign, encouraging gadget owners to migrate their code to the proper way of crafting well-cached thumbnail URLs and subsequently blocking thumb.php use on public wikis once the cleanup is complete.</i> </p> <div class="mw-heading mw-heading3"><h3 id="Private_wikis" data-mw-thread-id="h-Private_wikis-Broader_ecosystem"><span data-mw-comment-start="" id="h-Private_wikis-Broader_ecosystem"></span>Private wikis<span data-mw-comment-end="h-Private_wikis-Broader_ecosystem"></span></h3></div> <p>In the case of private wikis, Varnish doesn't cache thumbnails, because MediaWiki-level authentication is required to ensure that the client has access to the desired content (is logged into the private wiki). Therefore, Varnish passes the requests to MediaWiki, which verifies the user's credentials. Once authentication is validated, MediaWiki proxies the HTTP request to Thumbor. A shared secret key between MediaWiki and Thumbor is used to increase security. </p> <div class="mw-heading mw-heading3"><h3 id="Hitting_Thumbor_(common)" data-mw-thread-id="h-Hitting_Thumbor_(common)-Broader_ecosystem"><span id="Hitting_Thumbor_.28common.29"></span><span data-mw-comment-start="" id="h-Hitting_Thumbor_(common)-Broader_ecosystem"></span>Hitting Thumbor (common)<span data-mw-comment-end="h-Hitting_Thumbor_(common)-Broader_ecosystem"></span></h3></div> <p>When Thumbor receives a request, it tries to fetch the original media from Swift. If it can't, Swift will return a 404. Thumbor then proceeds to generate the request thumbnail for that media. Once it's done, Thumbor serves the resulting image, which the Swift proxy then forwards to Varnish, which serves it to the client. Varnish saves a copy in its own cache, and Thumbor saves a copy in Swift. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Ways_our_use_of_Thumbor_deviates_from_its_original_intent" data-mw-thread-id="h-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Ways our use of Thumbor deviates from its original intent<span data-mw-comment-end="h-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":["h-Disk_access-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","h-Filters-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","h-Image_processing_ordering-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","h-Subprocesses-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","h-Multi-engine_setup-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"]}}--></div> <div class="mw-heading mw-heading3"><h3 id="Disk_access" data-mw-thread-id="h-Disk_access-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Disk_access-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Disk access<span data-mw-comment-end="h-Disk_access-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h3></div> <p>Thumbor, in its default configuration, never touched the disk, for performance purposes. Since most image processing software isn't capable of streaming content, it keeps the originals entirely in memory in a request lifecycle. This works fine for most websites that deal with original media files that are a few megabytes at most. But the variety of files found on Wikimedia wikis mean we deal with some original files that are several gigabytes. This core logic in Thumbor of keeping originals in memory doesn't scale to the concurrency of large files we can experience. </p><p>This logic of passing the whole original around is deeply baked into Thumbor, which makes it difficult to change Thumbor itself to behave differently. Which is why we opted for a workaround, in the form of custom loaders. Loaders are a class of Thumbor plugins responsible for loading the original media from a given source. </p><p>Our custom loaders stream the original media coming from its source (eg. Swift) directly to a file on disk. The path of that file is then passed via a context variable, and the built-in variable in Thumbor that normally contains the whole original only contains the beginning of the file. Passing this extract allows us to leverage Thumbor's built-in logic for file type detection, because most file types signal what type they are at the beginning of the file. </p> <div class="mw-heading mw-heading3"><h3 id="Filters" data-mw-thread-id="h-Filters-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Filters-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Filters<span data-mw-comment-end="h-Filters-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h3></div> <p>Filters normally do something in Thumbor. We had needs, such as multipage support, that span very different engines. Which is why we repurposed filters to simply pass information to each engine, which is then responsible for applying the filter's functionality, instead of having logic for every possible engine baked into the filter. This deviates from Thumbor's intent to have filters do something, since not all engines have to do something according to a filter. </p> <div class="mw-heading mw-heading3"><h3 id="Image_processing_ordering" data-mw-thread-id="h-Image_processing_ordering-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Image_processing_ordering-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Image processing ordering<span data-mw-comment-end="h-Image_processing_ordering-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h3></div> <p>Thumbor tends to perform image operations right away (including filters), when it processes them. For performance and quality conservation purposes, we often queue those image operations and perform them all at once in a single command. This need is also increased by our reliance on subprocesses. </p> <div class="mw-heading mw-heading3"><h3 id="Subprocesses" data-mw-thread-id="h-Subprocesses-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Subprocesses-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Subprocesses<span data-mw-comment-end="h-Subprocesses-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h3></div> <p>Thumbor's default engines do everything with Python libraries. While this has the advantage of cleaner code, and doing everything in the same process, it has the disadvantage... of doing everything in the same process. On Wikimedia sites, we deal with a very wide variety of media. Some of the files would require too much memory to resize and can't be processed, some take too long. In the default Thumbor way of doing things, we could only set resource limits on the Thumbor process and no time limits because Thumbor is single-threaded (so a call to an operation on a python library can't be aborted). By doing all our image processing using subprocess commands, we have better control over resource and time limits for image processing. Which means that a given original being problematic is much less likely to take down the Thumbor process with it, or hog it, and other content can be processed. </p> <div class="mw-heading mw-heading3"><h3 id="Multi-engine_setup" data-mw-thread-id="h-Multi-engine_setup-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"><span data-mw-comment-start="" id="h-Multi-engine_setup-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span>Multi-engine setup<span data-mw-comment-end="h-Multi-engine_setup-Ways_our_use_of_Thumbor_deviates_from_its_original_intent"></span></h3></div> <p>Thumbor doesn't have infrastructure for multiple engines. It only expects a single engine as configuration and has a hardcoded special case for GIF. Due to this lack of generic multi-engine support, we developed our own using a proxy engine, which acts as the default Thumbor engine and routes requests to the various custom engines we've written. </p><p>We've also had to monkey-patch Thumbor's MIME type support to enable the new mime types supported by our various engines. Overall this is a weak area in Thumbor's extensibility that we had to work around, but changes could be made upstream to be more accommodating to our usage pattern. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Throttling" data-mw-thread-id="h-Throttling"><span data-mw-comment-start="" id="h-Throttling"></span>Throttling<span data-mw-comment-end="h-Throttling"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Throttling","replies":["h-Memcached-based-Throttling","h-Poolcounter-based-Throttling"]}}--></div> <p>In order to prevent abuse and to distribute server resources more fairly, Thumbor has a few throttling mechanisms in place. These happen as early as possible in the request handling, in order to avoid unnecessary work. </p> <div class="mw-heading mw-heading3"><h3 id="Memcached-based" data-mw-thread-id="h-Memcached-based-Throttling"><span data-mw-comment-start="" id="h-Memcached-based-Throttling"></span>Memcached-based<span data-mw-comment-end="h-Memcached-based-Throttling"></span></h3></div> <p>Failure throttling require having a memory of past events. For this we use <a href="/wiki/Memcached" class="mw-redirect" title="Memcached">Memcached</a>. In order to share the throttling information across Thumbor instances, we use a local <a href="/wiki/Nutcracker" title="Nutcracker">nutcracker</a> instance running on each Thumbor server, pointing to all the Thumbor servers in a given datacenter. This is configured in Puppet, with the list of servers in hiera under the <code>thumbor_memcached_servers</code> and <code>thumbor_memcached_servers_nutcracker</code> config variables. </p><p>In Thumbor's configuration, the memcached settings used for this are defined in <code>FAILURE_THROTTLING_MEMCACHE</code> and <code>FAILURE_THROTTLING_PREFIX</code>, found in Deployment Charts. </p> <div class="mw-heading mw-heading4"><h4 id="Failure" data-mw-thread-id="h-Failure-Memcached-based"><span data-mw-comment-start="" id="h-Failure-Memcached-based"></span>Failure<span data-mw-comment-end="h-Failure-Memcached-based"></span></h4></div> <p>The failure throttling logic itself is governed by the <code>FAILURE_THROTTLING_MAX</code> and <code>FAILURE_THROTTLING_DURATION</code> Thumbor config variables. This throttling limits retries on failing thumbnails. Some originals are broken or can't be rendered by our thumbnailing software and there would be no point retrying them every time we encounter them. This limit allows us to avoid rendering problematic originals for a while. We don't want to blacklist them permanently, however, as upgrading media-handling software might suddenly make originals that previously couldn't be rendered start working. This limit having an expiry guarantees that the benefits of upgrades apply naturally to problematic files, without requiring to clear a permanent blacklist whenever software is upgraded on the Thumbor hosts. </p> <div class="mw-heading mw-heading3"><h3 id="Poolcounter-based" data-mw-thread-id="h-Poolcounter-based-Throttling"><span data-mw-comment-start="" id="h-Poolcounter-based-Throttling"></span>Poolcounter-based<span data-mw-comment-end="h-Poolcounter-based-Throttling"></span></h3></div> <p>For other forms of throttling, we use <a href="/wiki/PoolCounter" title="PoolCounter">Poolcounter</a>. Both to combat malicious and unintentional DDoS, as well as regulate resource consumption. The Poolcounter server configuration shared by the different throttling types is defined in the <code>POOLCOUNTER_SERVER,</code> <code>POOLCOUNTER_PORT</code> and <code>POOLCOUNTER_RELEASE_TIMEOUT</code> Thumbor config variables, found in Deployment Charts. </p> <div class="mw-heading mw-heading4"><h4 id="Per-IP" data-mw-thread-id="h-Per-IP-Poolcounter-based"><span data-mw-comment-start="" id="h-Per-IP-Poolcounter-based"></span>Per-IP<span data-mw-comment-end="h-Per-IP-Poolcounter-based"></span></h4></div> <p>We limit the amount of concurrent thumbnail generation requests per client IP address. The configuration for that throttle is governed by the and <code>POOLCOUNTER_CONFIG_PER_IP</code> Thumbor config variable, found in Deployment Charts. </p> <div class="mw-heading mw-heading4"><h4 id="Per-original" data-mw-thread-id="h-Per-original-Poolcounter-based"><span data-mw-comment-start="" id="h-Per-original-Poolcounter-based"></span>Per-original<span data-mw-comment-end="h-Per-original-Poolcounter-based"></span></h4></div> <p>We limit the amount of concurrent thumbnail generation requests per original media. The configuration for that throttle is governed by the and <code>POOLCOUNTER_CONFIG_PER_ORIGINAL</code> Thumbor config variable, found in Deployment Charts. </p> <div class="mw-heading mw-heading4"><h4 id="Expensive" data-mw-thread-id="h-Expensive-Poolcounter-based"><span data-mw-comment-start="" id="h-Expensive-Poolcounter-based"></span>Expensive<span data-mw-comment-end="h-Expensive-Poolcounter-based"></span></h4></div> <p>Some thumbnail types are disproportionately expensive to render thumbnails for (in terms of CPU time, mostly). Those expensive types are subject to an extra throttle, defined by the <code>POOLCOUNTER_CONFIG_EXPENSIVE</code> Thumbor config variable, found in Deployment Charts. </p><p><b>Not per-user</b> </p><p>Unlike MediaWiki, Thumbor doesn't implement a per-user Poolcounter throttle. First because Thumbor has greater isolation (on purpose) and doesn't have access to any user data, including sessions. Secondly, the per-IP throttle should covers the same ground, as logged-in users should have little IP address variance during a session. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Logging" data-mw-thread-id="h-Logging"><span data-mw-comment-start="" id="h-Logging"></span>Logging<span data-mw-comment-end="h-Logging"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Logging","replies":[]}}--></div> <p>Thumbor logs go to stdout on the Thumbor containers. The logging configuration is defined in the deployment-charts repo, under the <code>THUMBOR_LOG_CONFIG</code> Thumbor config variable. </p><p>Thumbor logs also go to <a href="/wiki/Logstash" title="Logstash">Logstash</a>; one way to filter for them is <code>host:thumbor*</code>. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Configuration" data-mw-thread-id="h-Configuration"><span data-mw-comment-start="" id="h-Configuration"></span>Configuration<span data-mw-comment-end="h-Configuration"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Configuration","replies":["h-Scaling-Configuration"]}}--></div> <p>Thumbor consumes its configuration from the <code>/etc/thumbor.d/</code> folder. The .conf files found in that folder are parsed in alphabetical order by Thumbor. </p><p>Thumbor in Kubernetes is configured using our standard <a href="/wiki/Helmfile" class="mw-redirect" title="Helmfile">Helmfile</a> patterns. This means that all configuration lives in either the <a href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/deployment-charts/%2B/refs/heads/master/charts/thumbor/values.yaml" class="extiw" title="gerrit:plugins/gitiles/operations/deployment-charts/+/refs/heads/master/charts/thumbor/values.yaml">chart defaults</a> or in the <a href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/deployment-charts/%2B/refs/heads/master/helmfile.d/services/thumbor/" class="extiw" title="gerrit:plugins/gitiles/operations/deployment-charts/+/refs/heads/master/helmfile.d/services/thumbor/">helmfile.d directory for the service</a>. </p> <div class="mw-heading mw-heading3"><h3 id="Scaling" data-mw-thread-id="h-Scaling-Configuration"><span data-mw-comment-start="" id="h-Scaling-Configuration"></span>Scaling<span data-mw-comment-end="h-Scaling-Configuration"></span></h3></div> <p>To increase the capacity of Thumbor, increase the <code>replicas</code> parameter in the <a href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/deployment-charts/%2B/refs/heads/master/helmfile.d/services/thumbor/values.yaml#2" class="extiw" title="gerrit:plugins/gitiles/operations/deployment-charts/+/refs/heads/master/helmfile.d/services/thumbor/values.yaml">helmfile configuration</a> and redeploy. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Updating_custom_plugins" data-mw-thread-id="h-Updating_custom_plugins"><span data-mw-comment-start="" id="h-Updating_custom_plugins"></span>Updating custom plugins<span data-mw-comment-end="h-Updating_custom_plugins"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Updating_custom_plugins","replies":["h-Testing_the_changes-Updating_custom_plugins","h-Deployment-Updating_custom_plugins","h-Restarting-Updating_custom_plugins"]}}--></div> <p>Our custom Thumbor plugins have their reference repo at <a class="external free" href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/software/thumbor-plugins/">https://gerrit.wikimedia.org/r/plugins/gitiles/operations/software/thumbor-plugins/</a> </p> <div class="mw-heading mw-heading3"><h3 id="Testing_the_changes" data-mw-thread-id="h-Testing_the_changes-Updating_custom_plugins"><span data-mw-comment-start="" id="h-Testing_the_changes-Updating_custom_plugins"></span>Testing the changes<span data-mw-comment-end="h-Testing_the_changes-Updating_custom_plugins"></span></h3></div> <p>Before putting anything up for review, you can test your changes locally. You need to have Docker installed on your machine. Some tests, so-called online tests, require connecting to the internet, that's why the Docker container of these tests should be run with the internet connection. All the tests and the flake8 linter are simply run by calling <code>make docker_test</code> at the root of the thumbor plugins directory. </p><p>Once the tests pass locally and you push the changes to Gerrit for a review, both the linter and tests are automatically run via Jenkins. If you miss something related to the tests or linter locally, you will see an error message about it in Gerrit. </p> <div class="mw-heading mw-heading3"><h3 id="Deployment" data-mw-thread-id="h-Deployment-Updating_custom_plugins"><span data-mw-comment-start="" id="h-Deployment-Updating_custom_plugins"></span>Deployment<span data-mw-comment-end="h-Deployment-Updating_custom_plugins"></span></h3></div> <p>Thumbor uses the standard <a href="/wiki/Kubernetes/Deployments" title="Kubernetes/Deployments">Kubernetes service deployment pattern.</a> </p> <div class="mw-heading mw-heading3"><h3 id="Restarting" data-mw-thread-id="h-Restarting-Updating_custom_plugins"><span data-mw-comment-start="" id="h-Restarting-Updating_custom_plugins"></span>Restarting<span data-mw-comment-end="h-Restarting-Updating_custom_plugins"></span></h3></div> <p>Follow the <a href="/wiki/Kubernetes/Deployments#Rolling_restart" title="Kubernetes/Deployments">standard roll-restart method</a> for Kubernetes services. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Dashboards_and_logs" data-mw-thread-id="h-Dashboards_and_logs"><span data-mw-comment-start="" id="h-Dashboards_and_logs"></span>Dashboards and logs<span data-mw-comment-end="h-Dashboards_and_logs"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Dashboards_and_logs","replies":[]}}--></div> <p><a class="external text" href="https://grafana.wikimedia.org/d/Pukjw6cWk/thumbor?orgId=1">Grafana thumbor dashboard</a> </p><p><a class="external text" href="https://logstash.wikimedia.org/goto/181e3bec56099a8ab44db52a66984215">Logstash</a> </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Manhole" data-mw-thread-id="h-Manhole"><span data-mw-comment-start="" id="h-Manhole"></span>Manhole<span data-mw-comment-end="h-Manhole"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Manhole","replies":[]}}--></div> <p>Thumbor runs with python manhole for debugging/inspection purposes. See also <a class="external text" href="https://phabricator.wikimedia.org/T146143">T146143: Figure out a way to live-debug running production thumbor processes</a> </p><p>To invoke manhole, e.g. on thumbor on port 8827: </p> <pre> sudo -u thumbor socat - unix-connect:/srv/thumbor/tmp/thumbor@8827/manhole-8827 </pre> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Local_development" data-mw-thread-id="h-Local_development"><span data-mw-comment-start="" id="h-Local_development"></span>Local development<span data-mw-comment-end="h-Local_development"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Local_development","replies":[]}}--></div> <p>As of July 2022, we are running Debian inside the Docker container and now it is so easy to develop Thumbor plugins locally: </p><p><a class="external free" href="https://gerrit.wikimedia.org/g/operations/software/thumbor-plugins">https://gerrit.wikimedia.org/g/operations/software/thumbor-plugins</a> </p><p>Pull it, install Docker and run the development version of the project as such: </p> <pre>mylinux@DESKTOP-DW7K8KS:~/thumbor-plugins$ make build </pre> <p>You can find more information about the local configurations of Thumbor plugins in the <a href="https://gerrit.wikimedia.org/r/plugins/gitiles/operations/software/thumbor-plugins/%2B/refs/heads/master/README.md" class="extiw" title="gerrit:plugins/gitiles/operations/software/thumbor-plugins/+/refs/heads/master/README.md">README.md</a> file. </p><p><b>Question: how do you create sample thumbnail images to use in a test case?</b> </p><p>The Docker image can run the <code>thumbor</code> server standalone, which defaults to running on port 8800. To try the example below, make sure that the <code>FILE_LOADER_ROOT_PATH</code> configuration variable is set to <code>'/srv/service/tests/integration/originals'</code>. You need to define this in the thumbor.conf file, there is more info about it in the README.md file of the project. </p> <pre>mylinux@DESKTOP-DW7K8KS:~/thumbor-plugins$ wget http://localhost:8800/thumbor/unsafe/450x/Carrie.jpg --2023-01-20 11:00:22-- http://localhost:8800/thumbor/unsafe/450x/Carrie.jpg Resolving localhost (localhost)... 127.0.0.1 Connecting to localhost (localhost)|127.0.0.1|:8800... connected. HTTP request sent, awaiting response... 200 OK Length: 52069 (51K) [image/jpeg] Saving to: ‘Carrie.jpg’ Carrie.jpg 100%[============================================================================>] 50.85K --.-KB/s in 0s </pre> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="See_also" data-mw-thread-id="h-See_also"><span data-mw-comment-start="" id="h-See_also"></span>See also<span data-mw-comment-end="h-See_also"></span></h2><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-See_also","replies":[]}}--></div> <ul><li><a href="https://www.mediawiki.org/wiki/2022_Thumbor_updates" class="extiw" title="mw:2022 Thumbor updates">mw:2022 Thumbor updates</a></li></ul> <!-- NewPP limit report Parsed by mw‐web.eqiad.canary‐f44b675cf‐bq76m Cached time: 20241127140904 Cache expiry: 2592000 Reduced expiry: false Complications: [show‐toc] DiscussionTools time usage: 0.015 seconds CPU time usage: 0.063 seconds Real time usage: 0.073 seconds Preprocessor visited node count: 466/1000000 Post‐expand include size: 8004/2097152 bytes Template argument size: 1916/2097152 bytes Highest expansion depth: 8/100 Expensive parser function count: 0/500 Unstrip recursion depth: 0/20 Unstrip post‐expand size: 2778/5000000 bytes --> <!-- Transclusion expansion time report (%,ms,calls,template) 100.00% 29.569 1 -total 100.00% 29.569 1 Template:Navigation_Wikimedia_infrastructure 52.15% 15.421 1 Template:Navigation_sidebar --> <!-- Saved in parser cache with key labswiki:pcache:35071:|#|:idhash:canonical and timestamp 20241127140904 and revision id 2144228. Rendering was triggered because: page-view --> </div><!--esi <esi:include src="/esitest-fa8a495983347898/content" /> --><noscript><img src="https://login.wikimedia.org/wiki/Special:CentralAutoLogin/start?type=1x1" alt="" width="1" height="1" style="border: none; position: absolute;"></noscript> <div class="printfooter" data-nosnippet="">Retrieved from "<a dir="ltr" href="https://wikitech.wikimedia.org/w/index.php?title=Thumbor&oldid=2144228">https://wikitech.wikimedia.org/w/index.php?title=Thumbor&oldid=2144228</a>"</div></div> <div id="catlinks" class="catlinks catlinks-allhidden" data-mw="interface"></div> </div> </main> </div> <div class="mw-footer-container"> <footer id="footer" class="mw-footer" > <ul id="footer-info"> <li id="footer-info-lastmod"> This page was last edited on 31 January 2024, at 11:36.</li> <li id="footer-info-copyright">Text is available under the <a rel="nofollow" class="external text" href="https://creativecommons.org/licenses/by-sa/4.0/deed.en">Creative Commons Attribution-ShareAlike License</a>; additional terms may apply. See <a class="external text" href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Terms_of_Use">Terms of Use</a> for details.</li> </ul> <ul id="footer-places"> <li id="footer-places-privacy"><a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Privacy_policy">Privacy policy</a></li> <li id="footer-places-about"><a href="/wiki/Main_Page">About Wikitech</a></li> <li id="footer-places-disclaimers"><a href="https://foundation.wikimedia.org/wiki/General_disclaimer">Disclaimers</a></li> <li id="footer-places-wm-codeofconduct"><a href="https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct">Code of Conduct</a></li> <li id="footer-places-developers"><a href="https://developer.wikimedia.org">Developers</a></li> <li id="footer-places-statslink"><a href="https://stats.wikimedia.org/#/wikitech.wikimedia.org">Statistics</a></li> <li id="footer-places-cookiestatement"><a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Cookie_statement">Cookie statement</a></li> <li id="footer-places-mobileview"><a href="//wikitech.wikimedia.org/w/index.php?title=Thumbor&mobileaction=toggle_view_mobile" class="noprint stopMobileRedirectToggle">Mobile view</a></li> </ul> <ul id="footer-icons" class="noprint"> <li id="footer-copyrightico"><a href="https://wikimediafoundation.org/" class="cdx-button cdx-button--fake-button cdx-button--size-large cdx-button--fake-button--enabled"><img src="/static/images/footer/wikimedia-button.svg" width="84" height="29" alt="Wikimedia Foundation" loading="lazy"></a></li> <li id="footer-poweredbyico"><a href="https://www.mediawiki.org/" class="cdx-button cdx-button--fake-button cdx-button--size-large cdx-button--fake-button--enabled"><img src="/w/resources/assets/poweredby_mediawiki.svg" alt="Powered by MediaWiki" width="88" height="31" loading="lazy"></a></li> </ul> </footer> </div> </div> </div> <div class="vector-settings" id="p-dock-bottom"> <ul></ul> </div><script>(RLQ=window.RLQ||[]).push(function(){mw.config.set({"wgHostname":"mw-web.codfw.main-6df7948d6c-q5p2p","wgBackendResponseTime":121,"wgDiscussionToolsPageThreads":[{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Rationale","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Supported_file_types","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Broader_ecosystem","replies":[{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Public_wikis-Broader_ecosystem","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Private_wikis-Broader_ecosystem","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Hitting_Thumbor_(common)-Broader_ecosystem","replies":[]}]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Disk_access-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Filters-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Image_processing_ordering-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Subprocesses-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Multi-engine_setup-Ways_our_use_of_Thumbor_deviates_from_its_original_intent","replies":[]}]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Throttling","replies":[{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Memcached-based-Throttling","replies":[{"headingLevel":4,"name":"h-","type":"heading","level":0,"id":"h-Failure-Memcached-based","replies":[]}]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Poolcounter-based-Throttling","replies":[{"headingLevel":4,"name":"h-","type":"heading","level":0,"id":"h-Per-IP-Poolcounter-based","replies":[]},{"headingLevel":4,"name":"h-","type":"heading","level":0,"id":"h-Per-original-Poolcounter-based","replies":[]},{"headingLevel":4,"name":"h-","type":"heading","level":0,"id":"h-Expensive-Poolcounter-based","replies":[]}]}]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Logging","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Configuration","replies":[{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Scaling-Configuration","replies":[]}]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Updating_custom_plugins","replies":[{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Testing_the_changes-Updating_custom_plugins","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Deployment-Updating_custom_plugins","replies":[]},{"headingLevel":3,"name":"h-","type":"heading","level":0,"id":"h-Restarting-Updating_custom_plugins","replies":[]}]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Dashboards_and_logs","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Manhole","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Local_development","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-See_also","replies":[]}],"wgPageParseReport":{"discussiontools":{"limitreport-timeusage":"0.015"},"limitreport":{"cputime":"0.063","walltime":"0.073","ppvisitednodes":{"value":466,"limit":1000000},"postexpandincludesize":{"value":8004,"limit":2097152},"templateargumentsize":{"value":1916,"limit":2097152},"expansiondepth":{"value":8,"limit":100},"expensivefunctioncount":{"value":0,"limit":500},"unstrip-depth":{"value":0,"limit":20},"unstrip-size":{"value":2778,"limit":5000000},"timingprofile":["100.00% 29.569 1 -total","100.00% 29.569 1 Template:Navigation_Wikimedia_infrastructure"," 52.15% 15.421 1 Template:Navigation_sidebar"]},"cachereport":{"origin":"mw-web.eqiad.canary-f44b675cf-bq76m","timestamp":"20241127140904","ttl":2592000,"transientcontent":false}}});});</script> </body> </html>